<commit_before>var google = require ('googleapis');
var fs = require('fs');
//var readline = require('readline');
var readlineSync = require('readline-sync');

var depthpipe = 4;
var outpipe = "logstats.txt";
var titleLine = "\nchannelId , channelName , views , likes , ratio , duration , comments , age";
var re_depth = /--depth=(\d+)/i;
var re_out = /--output=([A-Za-z0-9_\.\-]+)/i;
process.argv.forEach(function(val) {
    var depthParam = val.match(re_depth);
    if(depthParam !== null){
        //console.log("type of param depth : "+ (typeof depthParam[1]));
        depthpipe = depthParam[1];
    }
    var outParam = val.match(re_out);
    if(outParam !== null){
        //console.log("type of param out : "+ (typeof outParam[1]));
        outpipe = outParam[1];

    }
    else if (val == "--help") {
        console.log("Proper Usage: node main.js");
        console.log("    --depth=n           Sets max depth of exploration per channel");
        console.log("    --output=filename   use file 'filename' for output");
        console.log("    --help              Help menu");
        console.log("");
        process.exit(0);
    }
});

try {
    var stats = fs.statSync(outpipe);
    console.log("output file exists");
    // Wait for user's response.
    var answer = readlineSync.question('Do you want to overwrite(o)?append(a)? ');
    if (answer == "o") {
        fs.writeFileSync(outpipe,titleLine,"utf8");
    }
} catch (err) {
    if(err.code == 'ENOENT') {
        console.log("output file doesn't exists, will be created");
        try {
            fs.writeFileSync(outpipe,titleLine,"utf8");
        }catch(e){
            console.log("could not write title line to file.");
        }
    } else {
        console.log("output file doesn't exists or something else happened : ", err.code);
        try {
            fs.writeFileSync(outpipe,titleLine,"utf8");
        }catch(e){
            console.log("could not write title line to file.");
        }
    }
}
var api_key_file;
try {
    api_key_file = fs.readFileSync('yt_api_key.txt','utf8');
} catch (e) {
    if (e.code === 'ENOENT') {
        console.error('File not found!');
    } else {
        throw e;
    }
}

var key_regexp = /[A-Za-z0-9\-]{30,}/;
var api_key = key_regexp.exec(api_key_file);
if (api_key === null) {
    console.error("google API key not found");
    process.exit(1);
}else{
    api_key = api_key[0];
    console.log("google API key : "+api_key);
}

google.options ({ auth: api_key });
var youtube = google.youtube ('v3');

var channel_ids = fs.readFileSync('yt_ids.txt').toString().split("\n");
while (channel_ids[channel_ids.length - 1] === "" || channel_ids[channel_ids.length - 1] == " ") {
    channel_ids.pop();
}
console.log(channel_ids.length+" channels loaded");

//variables de temporisation
var MAX_DAILY = 50000000;
var MAX_100S = 300000;
var MAX_20S  = Math.min(MAX_100S/100, MAX_DAILY/(24*3600))*20;
var quota_20s = 0;
var quota_total = 0;
var start_time = 0;
var last_20s_check = 0;
var quota_update = function(increm){
    quota_20s+=increm;
    quota_total+=increm;
};
var quota_check = function(){
    var now = Date.now();
    var exec_time = (now-start_time)/1000;
    if (exec_time == 0) {
        return true;
    }
    if ((now-last_20s_check)/1000 > 20) {
        console.log("testing 20s quota");

        last_20s_check = now;
        var tmpQuota = quota_20s;
        console.log("quota 20s : "+tmpQuota+"/"+MAX_20S);
        quota_20s = 0;
        if (tmpQuota > MAX_20S) {
            return false;
        }
    }
    if (quota_total/exec_time*20 > MAX_20S) {
        return false;
    }
    return true;
};

// Search Youtube -- callback is called on each found item
function search_youtube (query, callback) {

    var queryObject = {
        part: 'snippet',
        type: 'video',
        maxResults: 4,
        order: 'date',
        safeSearch: 'none'
    };
    if (query.q !== undefined) {
        queryObject.q = query.q;
    }
  if (query.pageToken !== undefined) {
      queryObject.pageToken = query.pageToken;
  }
  if (query.maxResults !==undefined) {
      queryObject.maxResults = query.maxResults;
  }if (query.channel !== undefined) {
      queryObject.channelId = query.channel;
  }
  var videos = [];
  var detailsLoop = function(res, index, errors, outfunction){
      //console.log(res);
      if (index >= res.length) {
          if (errors === '') {
              outfunction(null, videos);
          }else{
              outfunction(errors, videos);
          }

      }else{
          var video = {
            id: res[index].id.videoId,
            title: res[index].snippet.title || '',
            channelTitle: res[index].snippet.channelTitle,
            channelId: res[index].snippet.channelId,
            timeSincePublished: Math.floor((Date.now() - Date.parse(res[index].snippet.publishedAt))/1000)
          };
          console.log("Qt: "+quota_total+" fetching details for video "+ index + "("+video.title+")");

          if (quota_check() === false) {
              setTimeout(function(){console.log("temporisation");detailsLoop(res,index,errors,outfunction);},1000);
          }else{
              quota_update(1+2+2);
              youtube.videos.list (
                {
                  part: 'statistics, contentDetails',
                  id: video.id
                },
                function (err2, data) {
                  if (err2) { detailsLoop(res, index+1, errors+"\n"+err2, outfunction); }
                  if (data.items.length >= 1) {
                    data.items[0].contentDetails.duration.replace (/PT(\d+)M(\d+)S/, function (t, m, s) {
                      video.duration = (parseInt (m) *60) + parseInt (s);
                    });
                    video.definition = data.items[0].contentDetails.definition;
                      video.views = parseInt(data.items[0].statistics.viewCount);
                      video.likes = parseInt(data.items[0].statistics.likeCount);
                      video.dislikes = parseInt(data.items[0].statistics.dislikeCount);
                      video.comments = parseInt(data.items[0].statistics.commentCount);
                    videos.push(video);

                    detailsLoop(res, index+1, errors, outfunction);
                  }
                }
              );
          }

      }

  };
  if (quota_check() === false) {
      setTimeout(function(){console.log("temporisation");search_youtube(query, callback);},1000);
  }else{
      quota_update(100);
      youtube.search.list (
        queryObject,
        function (err, res) {
          if (err) { return callback (err); }

          detailsLoop(res.items, 0,'', callback);

        //   res.items.forEach (function (result) {
        //     var video = {
        //       id: result.id.videoId,
        //       //urlShort: 'http://youtu.be/'+ result.id.videoId,
        //       //urlLong: 'http://www.youtube.com/watch?v='+ result.id.videoId,
        //       //published: result.snippet.publishedAt,
        //       title: result.snippet.title || '',
        //       //description: result.snippet.description || '',
        //       //images: result.snippet.thumbnails,
        //       channelTitle: result.snippet.channelTitle,
        //       //channelId: result.snippet.channelId,
        //       //live: result.snippet.liveBroadcastContent || ''
        //     };
          //
        //     youtube.videos.list (
        //       {
        //         part: 'statistics, contentDetails',
        //         id: video.id
        //       },
        //       function (err2, data) {
        //         if (err2) { return callback (err2); }
        //         if (data.items.length >= 1) {
        //           data.items[0].contentDetails.duration.replace (/PT(\d+)M(\d+)S/, function (t, m, s) {
        //             video.duration = (parseInt (m) *60) + parseInt (s);
        //           });
        //           video.definition = data.items[0].contentDetails.definition;
        //             video.views = data.items[0].statistics.viewCount;
        //             video.likes = data.items[0].statistics.likeCount;
        //             video.dislikes = data.items[0].statistics.dislikeCount;
        //             video.comments = data.items[0].statistics.commentCount;
        //           videos.push(video);
        //         }
        //       }
        //     );
        //   });
        //   callback(videos.length)

        }
      );
  }
}

var like_view_analyzer = function(err, videos, callback){
    if (err !== null) {
        console.error(err);
        return;
    }
    var stats = {
        min_ratio : 10000,
        max_ratio : 0,
        avg_ratio : null
    };
    console.log("like/view analyzer start");
    var dataPoints = [];
    var cumulViews = 0;
    var cumulLikes = 0;
    //fs.appendFileSync(outpipe,"\nchannelName , views , likes , ratio , duration , comments , age", 'utf8');
    for (var i = 0; i < videos.length; i++) {
        var ratio = videos[i].likes/videos[i].views;
        if (ratio < stats.min_ratio) {
            stats.min_ratio = ratio;
        }
        if (ratio> stats.max_ratio) {
            stats.max_ratio = ratio;
        }
        cumulLikes+=videos[i].likes;
        cumulViews+=videos[i].views;
        //console.log(ratio + "\n" + "views : " + videos[i].views);
        var statsPoint = {
            channelId:videos[i].channelId,
            channelName:videos[i].channelTitle,
            views: videos[i].views,
            likes: videos[i].likes,
            ratio: ratio,
            duration : videos[i].duration,
            comments: videos[i].comments,
            age: videos[i].timeSincePublished
        };
        dataPoints.push(statsPoint);
        fs.appendFileSync(outpipe,"\n"+
        statsPoint.channelId+" , "+
        statsPoint.channelName+" , "+
        statsPoint.views+" , "+
        statsPoint.likes+" , "+
        statsPoint.ratio+" , "+
        statsPoint.duration+" , "+
        statsPoint.comments+" , "+
        statsPoint.age,
         'utf8');
    }
    stats.avg_ratio = cumulLikes/cumulViews;

    console.log(stats);
    callback();
};

//search sequence
start_time = Date.now();
//search_youtube({channel:'UCXIYLgIp6DYZHjmUUUXErmg', maxResults: 50},like_view_analyzer);

var defaultPost = function(err, videos, callback){
    if (err) {
        console.error(err);
    }
    if (videos) {
        console.log("loaded "+videos.length+" videos.");
    }
    callback();
};
var chainSearch = function(params){
    var options = {};
    options.depth = params.depth || 4;
    options.channelIds = params.channelIds || ['UCXIYLgIp6DYZHjmUUUXErmg'];
    options.postAction = params.postAction || defaultPost;
    var calendar = [];
    var scheduler = function(index){
        if (index == -1) {//init calendar
            console.log("initialising scheduling jobs...");
            for (var i = 0; i < options.channelIds.length; i++) {
                console.log("defining schedule for channel "+options.channelIds[i]);
                var func1 = function(j){
                    console.log("executing Task "+j+" on channel " + options.channelIds[j]);
                    search_youtube({channel:options.channelIds[j], maxResults: options.depth}, function(err, videos) {
                        options.postAction(err, videos, function(){
                            scheduler(j+1);
                        });
                    });
                };
                calendar.push(func1);
            }
            console.log("... Done.");
            scheduler(0);
        }else if(index < calendar.length){

            calendar[index](index);
        }else if (index >= calendar.length) {
            console.log("No job left. Exiting scheduler.");
        }
    };
    scheduler(-1);
};

chainSearch({depth: depthpipe, channelIds: channel_ids, postAction:like_view_analyzer});
<commit_msg>Async loading of files (output, api key, channels list), strict mode enabled, ES6 synthax adopted (arrow func, promises, block-scoped declaration)<commit_after>"use strict";

var google = require ('googleapis');
var fs = require('fs');
//var readline = require('readline');
var readlineSync = require('readline-sync');

const readline = require('readline');

var depthpipe = 4;
var outpipe = "logstats.txt";
var titleLine = "\nchannelId , channelName , views , likes , ratio , duration , comments , age";

var re_depth = /--depth=(\d+)/i;
var re_out = /--output=([A-Za-z0-9_\.\-]+)/i;

var youtube_api_key = null;
var youtube = null;

var channel_ids = ['UCXIYLgIp6DYZHjmUUUXErmg'];

//variables de temporisation
const MAX_DAILY = 50000000;
const MAX_100S = 300000;
const MAX_20S  = Math.min(MAX_100S/100, MAX_DAILY/(24*3600))*20;
var quota_20s = 0;
var quota_total = 0;
var start_time = 0;
var last_20s_check = 0;

//read arguments
process.argv.forEach(function(val) {
    var depthParam = val.match(re_depth);
    if(depthParam !== null){
        //console.log("type of param depth : "+ (typeof depthParam[1]));
        depthpipe = depthParam[1];
    }
    var outParam = val.match(re_out);
    if(outParam !== null){
        //console.log("type of param out : "+ (typeof outParam[1]));
        outpipe = outParam[1];

    }
    else if (val == "--help") {
        console.log("Proper Usage: node main.js");
        console.log("    --depth=n           Sets max depth of exploration per channel");
        console.log("    --output=filename   use file 'filename' for output");
        console.log("    --help              Help menu");
        console.log("");
        process.exit(0);
    }
});

var openFiles = function(){

    var criticalError = function(err){
        console.error("ERR : " + err);
        process.exit(1);
    };

    var outOpen = new Promise((resolve, reject) => {
        fs.stat(outpipe,(err, stats) => {
            if(err){
                if(err.code == 'ENOENT') {
                    console.log("output file doesn't exists, will be created");
                } else {
                    console.log("output file doesn't exists or something else happened : ", err.code);
                }
                fs.writeFile(outpipe,titleLine,"utf8", (err) => {
                    if (err) {
                        reject("could not write title line to file.");
                    }else{
                        resolve();
                    }
                });
            }else{
                console.log("output file exists");

                let rl = readline.createInterface({
                    input: process.stdin,
                    output: process.stdout
                });

                rl.question('Do you want to overwrite(o)?append(a)? ', (answer) => {
                    if (answer == "o") {
                        fs.writeFile(outpipe,titleLine,"utf8", (err) => {
                            if (err) {
                                reject("could not write title line to file.");
                            }else{
                                resolve();
                            }
                        });
                    }else{
                        resolve();
                    }
                rl.close();
                });
            }
        });
    })
    .then(() => {
        console.log("Success opening output file.");
        var apiKeyOpen = new Promise((resolve, reject) => {

            fs.readFile('yt_api_key.txt','utf8', (err,api_key_file) => {
                if(err){
                    if (err.code === 'ENOENT') {
                        reject('Key file not found!');
                    }else{
                        reject("unknown error opening api key file.");
                    }

                }else{
                    let key_regexp = /[A-Za-z0-9\-]{30,}/;
                    let api_key = key_regexp.exec(api_key_file);
                    if (api_key === null) {
                        reject("google API key not found in provided file");
                    }else{
                        resolve(api_key[0]);

                    }
                }
            });
        })
        .then((api_key) => {
            youtube_api_key = api_key;
            console.log("Success opening API key file.");

            var channelListOpen = new Promise((resolve, reject) => {
                fs.readFile('yt_ids.txt',"utf8",(err, data) => {
                    if (err) {
                        reject(err);
                    }else{
                        let channels = data.split("\n");
                        while (channels[channels.length - 1] === "" || channels[channels.length - 1] == " ") {
                            channels.pop();
                        }
                        console.log(channels.length+" channels loaded");
                        channel_ids = channels;
                        resolve();
                    }
                });

            })
            .then(API_init)
            .catch((err) => {
                console.error("non-critical error reading channel list :\n"+err);
                API_init();
            });
        })
        .catch(criticalError);

    })
    .catch(criticalError);

};

var API_init = function(){
    google.options ({ auth: youtube_api_key });
    youtube = google.youtube ('v3');
    console.log("API initialised");

    //start searching
    start_time = Date.now();
    chainSearch({depth: depthpipe, channelIds: channel_ids, postAction:like_view_analyzer});

};

var quota_update = function(increm){
    quota_20s+=increm;
    quota_total+=increm;
};
var quota_check = function(){
    var now = Date.now();
    var exec_time = (now-start_time)/1000;
    if (exec_time == 0) {
        return true;
    }
    if ((now-last_20s_check)/1000 > 20) {
        console.log("testing 20s quota");

        last_20s_check = now;
        var tmpQuota = quota_20s;
        console.log("quota 20s : "+tmpQuota+"/"+MAX_20S);
        quota_20s = 0;
        if (tmpQuota > MAX_20S) {
            return false;
        }
    }
    if (quota_total/exec_time*20 > MAX_20S) {
        return false;
    }
    return true;
};

// Search Youtube -- callback is called on each found item
function search_youtube (query, callback) {

    var queryObject = {
        part: 'snippet',
        type: 'video',
        maxResults: 4,
        order: 'date',
        safeSearch: 'none'
    };
    if (query.q !== undefined) {
        queryObject.q = query.q;
    }
    if (query.pageToken !== undefined) {
      queryObject.pageToken = query.pageToken;
    }
    if (query.maxResults !==undefined) {
      queryObject.maxResults = query.maxResults;
    }if (query.channel !== undefined) {
      queryObject.channelId = query.channel;
    }
    var videos = [];
    var detailsLoop = function(res, index, errors, outfunction){
        //console.log(res);
        if (index >= res.length) {
            if (errors === '') {
                outfunction(null, videos);
            }else{
                outfunction(errors, videos);
            }

        }else{
            var video = {
                id: res[index].id.videoId,
                title: res[index].snippet.title || '',
                channelTitle: res[index].snippet.channelTitle,
                channelId: res[index].snippet.channelId,
                timeSincePublished: Math.floor((Date.now() - Date.parse(res[index].snippet.publishedAt))/1000)
            };
            console.log("Qt: "+quota_total+" fetching details for video "+ index + "("+video.title+")");

            if (quota_check() === false) {
                setTimeout(function(){console.log("temporisation");detailsLoop(res,index,errors,outfunction);},1000);
            }else{
                quota_update(1+2+2);
                youtube.videos.list (
                {
                    part: 'statistics, contentDetails',
                    id: video.id
                },
                function (err2, data) {
                    if (err2) { detailsLoop(res, index+1, errors+"\n"+err2, outfunction); }
                    if (data.items.length >= 1) {
                        data.items[0].contentDetails.duration.replace (/PT(\d+)M(\d+)S/,
                            function (t, m, s) {
                                video.duration = (parseInt (m) *60) + parseInt (s);
                        });
                        video.definition = data.items[0].contentDetails.definition;
                        video.views = parseInt(data.items[0].statistics.viewCount);
                        video.likes = parseInt(data.items[0].statistics.likeCount);
                        video.dislikes = parseInt(data.items[0].statistics.dislikeCount);
                        video.comments = parseInt(data.items[0].statistics.commentCount);
                        videos.push(video);

                        detailsLoop(res, index+1, errors, outfunction);
                    }
                  }
                );
            }

        }

    };
    if (quota_check() === false) {
        setTimeout(function(){
            console.log("temporisation");
            search_youtube(query, callback);
        },1000);
    }else{
        quota_update(100);
        youtube.search.list (
            queryObject,
            function (err, res) {
                if (err) { return callback (err); }

                detailsLoop(res.items, 0,'', callback);

            }
        );
    }
}

var like_view_analyzer = function(err, videos, callback){
    if (err !== null) {
        console.error(err);
        return;
    }
    var stats = {
        min_ratio : 10000,
        max_ratio : 0,
        avg_ratio : null
    };
    console.log("like/view analyzer start");
    var dataPoints = [];
    var cumulViews = 0;
    var cumulLikes = 0;
    //fs.appendFileSync(outpipe,"\nchannelName , views , likes , ratio , duration , comments , age", 'utf8');
    for (var i = 0; i < videos.length; i++) {
        var ratio = videos[i].likes/videos[i].views;
        if (ratio < stats.min_ratio) {
            stats.min_ratio = ratio;
        }
        if (ratio> stats.max_ratio) {
            stats.max_ratio = ratio;
        }
        cumulLikes+=videos[i].likes;
        cumulViews+=videos[i].views;
        //console.log(ratio + "\n" + "views : " + videos[i].views);
        var statsPoint = {
            channelId:videos[i].channelId,
            channelName:videos[i].channelTitle,
            views: videos[i].views,
            likes: videos[i].likes,
            ratio: ratio,
            duration : videos[i].duration,
            comments: videos[i].comments,
            age: videos[i].timeSincePublished
        };
        dataPoints.push(statsPoint);
        fs.appendFileSync(outpipe,"\n"+
        statsPoint.channelId+" , "+
        statsPoint.channelName+" , "+
        statsPoint.views+" , "+
        statsPoint.likes+" , "+
        statsPoint.ratio+" , "+
        statsPoint.duration+" , "+
        statsPoint.comments+" , "+
        statsPoint.age,
         'utf8');
    }
    stats.avg_ratio = cumulLikes/cumulViews;

    console.log(stats);
    callback();
};

//search sequence
// start_time = Date.now();
//search_youtube({channel:'UCXIYLgIp6DYZHjmUUUXErmg', maxResults: 50},like_view_analyzer);

var defaultPost = function(err, videos, callback){
    if (err) {
        console.error(err);
    }
    if (videos) {
        console.log("loaded "+videos.length+" videos.");
    }
    callback();
};
var chainSearch = function(params){
    var options = {};
    options.depth = params.depth || 4;
    options.channelIds = params.channelIds || ['UCXIYLgIp6DYZHjmUUUXErmg'];
    options.postAction = params.postAction || defaultPost;
    var calendar = [];
    var scheduler = function(index){
        if (index == -1) {//init calendar
            console.log("initialising scheduling jobs...");
            for (var i = 0; i < options.channelIds.length; i++) {
                console.log("defining schedule for channel "+options.channelIds[i]);
                var func1 = function(j){
                    console.log("executing Task "+j+" on channel " + options.channelIds[j]);
                    search_youtube({channel:options.channelIds[j], maxResults: options.depth}, function(err, videos) {
                        options.postAction(err, videos, function(){
                            scheduler(j+1);
                        });
                    });
                };
                calendar.push(func1);
            }
            console.log("... Done.");
            scheduler(0);
        }else if(index < calendar.length){

            calendar[index](index);
        }else if (index >= calendar.length) {
            console.log("No job left. Exiting scheduler.");
        }
    };
    scheduler(-1);
};

openFiles();
<|endoftext|>


<commit_before>/*
 * Evernote-webhooks: A project to use webhooks to automate things in Evernote
 */
var express = require('express');
var Evernote = require('evernote').Evernote;
var expressSession = require('express-session');
var UsersMap = require('./UsersMap');

var config;
try {
  config = require('./config.json');
} catch (e) {
  config = {};
}

config.API_CONSUMER_KEY = config.API_CONSUMER_KEY || process.env.consumerKey;
config.API_CONSUMER_SECRET = config.API_CONSUMER_SECRET || process.env.consumerSecret;
config.SANDBOX = config.SANDBOX || true;
config.SERVICE_BASE = config.SERVICE_BASE || process.env.serviceBase;
config.TARGET_TAG_NAME = 'toc';

var oauthCallbackUrl = config.SERVICE_BASE + '/OAuthCallback';

var usersMap = new UsersMap();
var history = [];

/**
 * Function to log an error and send it to res, or redirect to some target url
 */
var errorLogger = function(res, error, targetUrl) {
  console.log(error);
  if (typeof targetUrl === 'string') {
    res.redirect(targetUrl);
  } else {
    res.send(error);
  }
}

var app = express();
var wwwDir = '/www';

app.use(expressSession({
  resave: false,
  saveUninitialized: false,
  secret: 'this is a secret'
}));
app.use('/', express.static(__dirname + wwwDir));

app.get('/', function(req, res) { res.render(wwwDir + '/index.html'); });

app.get('/error', function(req, res) {
  console.log('/error');
  console.log(req.session);
  res.send('An error occurred. Womp womp');
});

/**
 * The hook to receive Evernote events:
 * https://dev.evernote.com/doc/articles/polling_notification.php
 * Logs the query to the history array
 * For our purposes, we only care about responding to note update events.
 */
app.get('/hook', function(req, res) {
  console.log('/hook');
  history.push(req.query);
  if (req.query && req.query.userId && req.query.guid
    && (req.query.reason === 'update' || req.query.reason === 'business_update')) {
    // TODO: Do we really need to check the reason? Is just having the guid enough?
    //   (ie, in case a client adds a note with a tag)
    // Retrieve the note name stored in session if we have saved it
    // TODO: Delete this when we don't need to fake our hooks
    var noteTitle = '';
    if (req.query.guid === req.session.noteGuid) {
      noteTitle = req.session.noteTitle;
    }

    // Look up this userID and get note info for this note
    var userInfo = usersMap.getInfoForUser(req.query.userId);
    if (userInfo && userInfo.oauthAccessToken) {
      var client = new Evernote.Client({
        token: userInfo.oauthAccessToken,
        sandbox: config.SANDBOX
      });
      var noteStoreClient = client.getNoteStore();
      noteStoreClient.getNoteTagNames(userInfo.oauthAccessToken, req.query.guid,
        function(error, tagNames) {
          if (error) {
            errorLogger(res, error);
            return;
          } else {
            // Try to find the target tag in the tags of this note.
            var upperCaseTagNames = tagNames.map(function(a) { return a.toUpperCase(); });
            if (upperCaseTagNames.indexOf(config.TARGET_TAG_NAME.toUpperCase()) > -1) {
              res.send('Note ' + noteTitle + ' has tag!');
              // TODO: Do some automation here!
            } else {
              res.send('Note ' + (noteTitle ? noteTitle : req.query.guid) +
                ' does not have tag ' + config.TARGET_TAG_NAME);
            }
          }
        }
      );
    }
  } else {
    res.send('query is not a note update')
  }
});

/**
 * Lists all queries that we've captured in our history
 */
app.get('/history', function(req, res) {
  console.log('/history');
  var response = '';
  for (var i in history) {
    response += 'request ' + i + ': '
    response += JSON.stringify(history[i]);
    response += '
'
  }
  res.send(response);
});

/**
 * Logs some notes in case we want to inspect the details
 */
app.get('/listNotes', function(req, res) {
  console.log('/listNotes');
  if (req.query && req.query.userId) {
    var userInfo = usersMap.getInfoForUser(req.query.userId);
    if (userInfo) {
      var noteFilter = new Evernote.NoteFilter({
        order: Evernote.NoteSortOrder.UPDATE_SEQUENCE_NUMBER,
        inactive: false,
        ascending: false, // newest first
        words: ''
      });

      var resultSpec = new Evernote.NotesMetadataResultSpec({
        includeTitle: true,
        includeTagGuids: true,
        includeNotebookGuid: true,
        includeDeleted: true,
        includeAttributes: true
      });

      var client = new Evernote.Client({
        token: userInfo.oauthAccessToken,
        sandbox: config.SANDBOX
      });
      var noteStoreClient = client.getNoteStore();
      noteStoreClient.findNotesMetadata(userInfo.oauthAccessToken, noteFilter, 0, 100,
        resultSpec, function(error, noteList) {
          if (error) {
            errorLogger(res, error);
            return;
          }
          console.log(noteList.notes);

          if (noteList.notes.length) {
            response = '';
            response += noteList.notes.length + ' notes found
';
            response += 'first note:
';
            response += 'guid: ' + noteList.notes[0].guid + '
';
            response += 'title: ' + noteList.notes[0].title + '
';
            response += 'notebookGuid: ' + noteList.notes[0].notebookGuid + '
';
            response += 'tagGuids: ' + noteList.notes[0].tagGuids + '
';
            response += '"update" this note';

            // Add the guid and note name to this session in case the user clicks the link.
            // TODO: Delete this when we don't need to fake our hooks
            req.session.noteGuid = noteList.notes[0].guid;
            req.session.noteTitle = noteList.notes[0].title;
            res.send(response);
          } else {
            res.send('No notes found!');
          }
        });

      return;
    }
  }
  res.send('User not found. Try OAuth');
});

/**
 * Begins the OAuth process. Based on
 * https://github.com/evernote/evernote-sdk-js/blob/master/sample/express/routes/index.js
 */
app.get('/OAuth', function(req, res) {
  console.log('/OAuth');
  var client = new Evernote.Client({
    consumerKey: config.API_CONSUMER_KEY,
    consumerSecret: config.API_CONSUMER_SECRET,
    sandbox: config.SANDBOX
  });

  client.getRequestToken(oauthCallbackUrl,
    function(error, oauthRequestToken, oauthRequestTokenSecret, results){
      if (error) {
        errorLogger(res, error);
        return;
      } else {
        // store the tokens in the session
        req.session.oauthRequestToken = oauthRequestToken;
        req.session.oauthRequestTokenSecret = oauthRequestTokenSecret;

        // redirect the user to authorize the token
        res.redirect(client.getAuthorizeUrl(oauthRequestToken));
      }
    }
  );
});

/**
 * Once we have an OAuth request token, we
 */
app.get('/OAuthCallback', function(req, res) {
  console.log('/OAuthCallback');
  var client = new Evernote.Client({
    consumerKey: config.API_CONSUMER_KEY,
    consumerSecret: config.API_CONSUMER_SECRET,
    sandbox: config.SANDBOX
  });

  client.getAccessToken(
    req.session.oauthRequestToken,
    req.session.oauthRequestTokenSecret,
    req.query['oauth_verifier'],
    function(error, oauthAccessToken, oauthAccessTokenSecret, results) {
      if (error) {
        errorLogger(res, error, '/error');
        return;
      } else {
        // store the access token in the session
        usersMap.addUser(results.edam_userId, oauthAccessToken, oauthAccessTokenSecret,
          results.edam_expires);
        usersMap.setShardForUser(results.edam_userId, results.edam_shard);
        usersMap.setNoteStoreUrlForUser(results.edam_userId, results.edam_noteStoreUrl);
        usersMap.setWebApiUrlPrefixForUser(results.edam_userId,
          results.edam_webApiUrlPrefix);

        req.session.oauthDone = true;
        req.session.userId = results.edam_userId;
        res.redirect('/OAuthDone');
      }
    }
  );
});

app.get('/OAuthDone', function(req, res) {
  console.log('/OAuthDone: ' + req.session.oauthDone);
  if (req.session.oauthDone) {
    res.send('OAuth complete! List notes');
  } else {
    res.send('Error with OAuth; check the logs.');
  }
});

// Start the server on port 3000 or the server port.
var port = process.env.PORT || 3000;
console.log('PORT: ' + port);
var server = app.listen(port);
<commit_msg>Fetch all notes with tag in same notebook when note with tag updated<commit_after>/*
 * Evernote-webhooks: A project to use webhooks to automate things in Evernote
 */
var express = require('express');
var Evernote = require('evernote').Evernote;
var expressSession = require('express-session');
var UsersMap = require('./UsersMap');

var config;
try {
  config = require('./config.json');
} catch (e) {
  config = {};
}

config.API_CONSUMER_KEY = config.API_CONSUMER_KEY || process.env.consumerKey;
config.API_CONSUMER_SECRET = config.API_CONSUMER_SECRET || process.env.consumerSecret;
config.SANDBOX = config.SANDBOX || true;
config.SERVICE_BASE = config.SERVICE_BASE || process.env.serviceBase;
config.TARGET_TAG_NAME = 'toc';

var oauthCallbackUrl = config.SERVICE_BASE + '/OAuthCallback';

var usersMap = new UsersMap();
var history = [];

/**
 * Function to log an error and send it to res, or redirect to some target url
 */
var errorLogger = function(res, error, targetUrl) {
  console.log(error);
  if (typeof targetUrl === 'string') {
    res.redirect(targetUrl);
  } else {
    res.send(error);
  }
}

var app = express();
var wwwDir = '/www';

app.use(expressSession({
  resave: false,
  saveUninitialized: false,
  secret: 'this is a secret'
}));
app.use('/', express.static(__dirname + wwwDir));

app.get('/', function(req, res) { res.render(wwwDir + '/index.html'); });

app.get('/error', function(req, res) {
  console.log('/error');
  console.log(req.session);
  res.send('An error occurred. Womp womp');
});

/**
 * The hook to receive Evernote events:
 * https://dev.evernote.com/doc/articles/polling_notification.php#webhooks
 * Logs the query to the history array
 * For our purposes, we only care about responding to note update events.
 */
app.get('/hook', function(req, res) {
  console.log('/hook');
  history.push(req.query);
  if (req.query && req.query.userId && req.query.guid && req.query.notebookGuid
    && (req.query.reason === 'update' || req.query.reason === 'business_update')) {
    // TODO(3): Do we really need to check the reason? Is just having the guid enough?
    //   (ie, in case a client adds a note with a tag)
    // Retrieve the note name stored in session if we have saved it
    // TODO(2): Delete this when we don't need to fake our hooks
    var noteTitle = '';
    if (req.query.guid === req.session.noteGuid) {
      noteTitle = req.session.noteTitle;
    }

    // Look up this userID and get note info for this note
    var userInfo = usersMap.getInfoForUser(req.query.userId);
    if (userInfo && userInfo.oauthAccessToken) {
      // TODO(3): Save this client on the userInfo object
      var client = new Evernote.Client({
        token: userInfo.oauthAccessToken,
        sandbox: config.SANDBOX
      });
      var noteStoreClient = client.getNoteStore();
      noteStoreClient.getNoteTagNames(userInfo.oauthAccessToken, req.query.guid,
        function(error, tagNames) {
          if (error) {
            errorLogger(res, error);
            return;
          } else {
            // Try to find the target tag in the tags of this note.
            var upperCaseTagNames = tagNames.map(function(a) { return a.toUpperCase(); });
            if (upperCaseTagNames.indexOf(config.TARGET_TAG_NAME.toUpperCase()) > -1) {
              var noteFilter = new Evernote.NoteFilter({
                order: Evernote.NoteSortOrder.UPDATE, // sort by last updated time
                inactive: false,
                ascending: true, // oldest first
                notebookGuid: req.query.notebookGuid,
                words: 'tag:' + config.TARGET_TAG_NAME
              });

              var resultSpec = new Evernote.NotesMetadataResultSpec({
                includeTitle: true
              });

              noteStoreClient.findNotesMetadata(userInfo.oauthAccessToken, noteFilter, 0,
                100, resultSpec, function(error, noteList) {
                  if (error) {
                    errorLogger(res, error);
                    return;
                  }
                  // TODO(2): Check noteList.totalNotes to see if we need to call this again or not
                  if (noteList.notes) {
                    console.log(noteList.notes);
                    var noteLinkList = noteList.notes.reduce(function(partialList, note) {
                      var noteUrl = 'evernote:///view/' + userInfo.userId + '/' + userInfo.shard
                              + '/' + note.guid + '/' + note.guid + '/';
                      return partialList + '
' + note.title + '
';
                    }, '');
                    // TODO(2): Look for an existing ToC note
                    // TODO(1): Create a ToC note with this information
                    res.send(noteLinkList);
                  } else {
                    // This should never happen
                    console.log('No notes found!');
                    res.send('No notes with tag found!');
                  }
                });
            } else {
              res.send('Note ' + (noteTitle ? noteTitle : req.query.guid) +
                ' does not have tag ' + config.TARGET_TAG_NAME);
            }
          }
        }
      );
    }
  } else {
    res.send('query is not a note update')
  }
});

/**
 * Lists all queries that we've captured in our history
 */
app.get('/history', function(req, res) {
  console.log('/history');
  var response = '';
  for (var i in history) {
    response += 'request ' + i + ': '
    response += JSON.stringify(history[i]);
    response += '
'
  }
  res.send(response);
});

/**
 * Logs some notes in case we want to inspect the details. Mainly for testing.
 * Provides a link that will fake an update webhook event for a given note.
 */
app.get('/listNotes', function(req, res) {
  console.log('/listNotes');
  if (req.query && req.query.userId) {
    var userInfo = usersMap.getInfoForUser(req.query.userId);
    if (userInfo) {
      var noteFilter = new Evernote.NoteFilter({
        order: Evernote.NoteSortOrder.UPDATE_SEQUENCE_NUMBER,
        inactive: false,
        ascending: false, // newest first
        words: ''
      });

      var resultSpec = new Evernote.NotesMetadataResultSpec({
        includeTitle: true,
        includeTagGuids: true,
        includeNotebookGuid: true
      });

      var client = new Evernote.Client({
        token: userInfo.oauthAccessToken,
        sandbox: config.SANDBOX
      });
      var noteStoreClient = client.getNoteStore();
      noteStoreClient.findNotesMetadata(userInfo.oauthAccessToken, noteFilter, 0, 100,
        resultSpec, function(error, noteList) {
          if (error) {
            errorLogger(res, error);
            return;
          }
          console.log(noteList.notes);

          if (noteList.notes.length) {
            response = '';
            response += noteList.notes.length + ' notes found
';
            response += 'first note:
';
            response += 'guid: ' + noteList.notes[0].guid + '
';
            response += 'title: ' + noteList.notes[0].title + '
';
            response += 'notebookGuid: ' + noteList.notes[0].notebookGuid + '
';
            response += 'tagGuids: ' + noteList.notes[0].tagGuids + '
';
            response += '"update" this note';

            // Add the guid and note name to this session in case the user clicks the link.
            // TODO(2): Delete this when we don't need to fake our hooks
            req.session.noteGuid = noteList.notes[0].guid;
            req.session.noteTitle = noteList.notes[0].title;
            res.send(response);
          } else {
            res.send('No notes found!');
          }
        });

      return;
    }
  }
  res.send('User not found. Try OAuth');
});

/**
 * Begins the OAuth process. Based on
 * https://github.com/evernote/evernote-sdk-js/blob/master/sample/express/routes/index.js
 */
app.get('/OAuth', function(req, res) {
  console.log('/OAuth');
  var client = new Evernote.Client({
    consumerKey: config.API_CONSUMER_KEY,
    consumerSecret: config.API_CONSUMER_SECRET,
    sandbox: config.SANDBOX
  });

  client.getRequestToken(oauthCallbackUrl,
    function(error, oauthRequestToken, oauthRequestTokenSecret, results){
      if (error) {
        errorLogger(res, error);
        return;
      } else {
        // store the tokens in the session
        req.session.oauthRequestToken = oauthRequestToken;
        req.session.oauthRequestTokenSecret = oauthRequestTokenSecret;

        // redirect the user to authorize the token
        res.redirect(client.getAuthorizeUrl(oauthRequestToken));
      }
    }
  );
});

/**
 * Once we have an OAuth request token, we
 */
app.get('/OAuthCallback', function(req, res) {
  console.log('/OAuthCallback');
  var client = new Evernote.Client({
    consumerKey: config.API_CONSUMER_KEY,
    consumerSecret: config.API_CONSUMER_SECRET,
    sandbox: config.SANDBOX
  });

  client.getAccessToken(
    req.session.oauthRequestToken,
    req.session.oauthRequestTokenSecret,
    req.query['oauth_verifier'],
    function(error, oauthAccessToken, oauthAccessTokenSecret, results) {
      if (error) {
        errorLogger(res, error, '/error');
        return;
      } else {
        // store the access token in the session
        usersMap.addUser(results.edam_userId, oauthAccessToken, oauthAccessTokenSecret,
          results.edam_expires);
        usersMap.setShardForUser(results.edam_userId, results.edam_shard);
        usersMap.setNoteStoreUrlForUser(results.edam_userId, results.edam_noteStoreUrl);
        usersMap.setWebApiUrlPrefixForUser(results.edam_userId,
          results.edam_webApiUrlPrefix);

        req.session.oauthDone = true;
        req.session.userId = results.edam_userId;
        res.redirect('/OAuthDone');
      }
    }
  );
});

app.get('/OAuthDone', function(req, res) {
  console.log('/OAuthDone: ' + req.session.oauthDone);
  if (req.session.oauthDone) {
    res.send('OAuth complete! List notes');
  } else {
    res.send('Error with OAuth; check the logs.');
  }
});

// Start the server on port 3000 or the server port.
var port = process.env.PORT || 3000;
console.log('PORT: ' + port);
var server = app.listen(port);
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    yaml = require('js-yaml');

module.exports = (function () {
    'use strict';
    return {

        /**
         * Loads a wrangler configuration file (which is simply a *.json, *.js, or *.yaml file).
         * @param file {String} - File path.
         * @returns {Object|*} - Returns the loaded returned value of loading `file` or file path
         *  if it does not match one of the allowed file types [json,js,yaml].
         */
        loadConfigFile: function (file) {
            if (file.lastIndexOf('.js') === file.length - 3
                || file.lastIndexOf('.json') === file.length - 5) {
                file = require(file);
            }
            else if (file.lastIndexOf('.yaml') === file.length - 5) {
                file = yaml.safeLoad(fs.readFileSync(file));
            }
            return file;
        },

        /**
         * Writes a configuration file depending on file extension in the `filePath` parameter.
         * @param obj {Object} - Object to convert to file.
         * @param filePath {String} - File path to write `obj` to.
         * @returns {exports}
         */
        writeConfigFile: function (obj, filePath) {
            if (filePath.lastIndexOf('.json') === file.length - 5) {
                obj = JSON.stringify(obj, '    ');
            }
            else if (filePath.lastIndexOf('.yaml') === file.length - 5) {
                obj = yaml.safeDump(obj);
            }
            else if (filePath.lastIndexOf('.js') === file.length - 3) {
                obj = '\'use strict\'; module.exports = ' + JSON.stringify(obj, '    ') + ';';
            }
            fs.writeFileSync(filePath, obj);
            return this;
        }

    };
}());<commit_msg>Removed uneeded file (will probably separate it as a utility later on).<commit_after><|endoftext|>


<commit_before>'use strict';
const path     = require('path');
const fs       = require('fs');
const electron = require('electron');
const appMenu  = require('./menu');
const config   = require('./config');
const tray     = require('./tray');
const version  = require('./version');

const app      = electron.app;
const ipcMain  = electron.ipcMain;

require('electron-debug')();

const BrowserWindow = electron.BrowserWindow;

let mainWindow;
let isQuitting = false;

const isAlreadyRunning = app.makeSingleInstance(() => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }

    mainWindow.webContents.on('did-finish-load', function() {
      setTimeout(function() {
          mainWindow.show()
      }, 60)
    })
  }
});

if (isAlreadyRunning) {
  app.quit();
}

function createMainWindow() {
  const lastWindowState = config.get('lastWindowState');
  const isDarkMode = config.get('darkMode');
  const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1';
  const rammeDesktopIcon = path.join(__dirname, 'static/icon.png');
  const maxWidthValue = 550;
  const minWidthValue = 400;

  // Create the browser window.
  const win = new BrowserWindow({
    title: app.getName(),
    show: false,
    x: lastWindowState.x,
    y: lastWindowState.y,
    minHeight: 400,
    minWidth: minWidthValue,
    maxWidth: maxWidthValue,
    width: lastWindowState.width,
    height: lastWindowState.height,
    maximizable: false,
    fullscreenable: false,
    icon: process.platform === 'linux' && rammeDesktopIcon,
    titleBarStyle: 'hidden-inset',
    darkTheme: isDarkMode,
    backgroundColor: isDarkMode ? '#192633' : '#fff',
    autoHideMenuBar: true,
    webPreferences: {
      preload: path.join(__dirname, 'browser.js'),
      nodeIntegration: false
    }
  });

  win.webContents.setUserAgent(userAgent);
  win.loadURL(`https://www.instagram.com`);

  win.on('close', e => {
    if (!isQuitting) {
      e.preventDefault();

      if (process.platform === 'darwin') {
        app.hide();
      } else {
        win.hide();
      }
    }
  });

  win.on('page-title-updated', e => {
    e.preventDefault();
    tray.create(win);
  });

  return win;
}

function sendAction(action) {
  const win = BrowserWindow.getAllWindows()[0];

  if (process.platform === 'darwin') {
    win.restore();
  }

  win.webContents.send(action);
}

app.on('ready', () => {
  electron.Menu.setApplicationMenu(appMenu);
  mainWindow = createMainWindow();

  const page = mainWindow.webContents;

  ipcMain.on('back', (event, arg) => {
    if (page.canGoBack()) {
      page.goBack();
    }
  });

  page.on('did-navigate-in-page', (event, arg) => {
    const menuBackBtn = appMenu.items[1].submenu.items[0];
    menuBackBtn.enabled = page.canGoBack();
    page.send('set-button-state', menuBackBtn.enabled);
  });

  page.on('dom-ready', () => {
    page.insertCSS(fs.readFileSync(path.join(__dirname, 'browser.css'), 'utf8'));
    page.insertCSS(fs.readFileSync(path.join(__dirname, 'dark-browser.css'), 'utf8'));
    mainWindow.show();
  });

  page.on('new-window', (e, url) => {
    e.preventDefault();
    electron.shell.openExternal(url);
  });

  setTimeout(function() {version.check()}, 8000);
});

app.on('activate', () => {
  mainWindow.show();
});

app.on('before-quit', () => {
  isQuitting = true;

  config.set('lastWindowState', mainWindow.getBounds());
});
<commit_msg>Added comment for future me<commit_after>'use strict';
const path     = require('path');
const fs       = require('fs');
const electron = require('electron');
const appMenu  = require('./menu');
const config   = require('./config');
const tray     = require('./tray');
const version  = require('./version');

const app      = electron.app;
const ipcMain  = electron.ipcMain;

require('electron-debug')();

const BrowserWindow = electron.BrowserWindow;

let mainWindow;
let isQuitting = false;

const isAlreadyRunning = app.makeSingleInstance(() => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore();
    }

    // Prevent flash on startup when in dark-mode
    mainWindow.webContents.on('did-finish-load', function() {
      setTimeout(function() {
          mainWindow.show()
      }, 60)
    })
  }
});

if (isAlreadyRunning) {
  app.quit();
}

function createMainWindow() {
  const lastWindowState = config.get('lastWindowState');
  const isDarkMode = config.get('darkMode');
  const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1';
  const rammeDesktopIcon = path.join(__dirname, 'static/icon.png');
  const maxWidthValue = 550;
  const minWidthValue = 400;

  // Create the browser window.
  const win = new BrowserWindow({
    title: app.getName(),
    show: false,
    x: lastWindowState.x,
    y: lastWindowState.y,
    minHeight: 400,
    minWidth: minWidthValue,
    maxWidth: maxWidthValue,
    width: lastWindowState.width,
    height: lastWindowState.height,
    maximizable: false,
    fullscreenable: false,
    icon: process.platform === 'linux' && rammeDesktopIcon,
    titleBarStyle: 'hidden-inset',
    darkTheme: isDarkMode,
    backgroundColor: isDarkMode ? '#192633' : '#fff',
    autoHideMenuBar: true,
    webPreferences: {
      preload: path.join(__dirname, 'browser.js'),
      nodeIntegration: false
    }
  });

  win.webContents.setUserAgent(userAgent);
  win.loadURL(`https://www.instagram.com`);

  win.on('close', e => {
    if (!isQuitting) {
      e.preventDefault();

      if (process.platform === 'darwin') {
        app.hide();
      } else {
        win.hide();
      }
    }
  });

  win.on('page-title-updated', e => {
    e.preventDefault();
    tray.create(win);
  });

  return win;
}

function sendAction(action) {
  const win = BrowserWindow.getAllWindows()[0];

  if (process.platform === 'darwin') {
    win.restore();
  }

  win.webContents.send(action);
}

app.on('ready', () => {
  electron.Menu.setApplicationMenu(appMenu);
  mainWindow = createMainWindow();

  const page = mainWindow.webContents;

  ipcMain.on('back', (event, arg) => {
    if (page.canGoBack()) {
      page.goBack();
    }
  });

  page.on('did-navigate-in-page', (event, arg) => {
    const menuBackBtn = appMenu.items[1].submenu.items[0];
    menuBackBtn.enabled = page.canGoBack();
    page.send('set-button-state', menuBackBtn.enabled);
  });

  page.on('dom-ready', () => {
    page.insertCSS(fs.readFileSync(path.join(__dirname, 'browser.css'), 'utf8'));
    page.insertCSS(fs.readFileSync(path.join(__dirname, 'dark-browser.css'), 'utf8'));
    mainWindow.show();
  });

  page.on('new-window', (e, url) => {
    e.preventDefault();
    electron.shell.openExternal(url);
  });

  setTimeout(function() {version.check()}, 8000);
});

app.on('activate', () => {
  mainWindow.show();
});

app.on('before-quit', () => {
  isQuitting = true;

  config.set('lastWindowState', mainWindow.getBounds());
});
<|endoftext|>


<commit_before>TankJS.addComponent("Image")

.tags("Drawable")

.includes("Pos2D")

.initFunction(function()
{
  this.zdepth = 0;
  this._image = new Image();

  addProperty(this, "imagePath",
    function()
    {
      return this._image.src;
    },
    function(val)
    {
      this._image.src = val;
    });

  addProperty(this, "width",
    function()
    {
      return this._image.width;
    },
    function(val)
    {
    });

  addProperty(this, "height",
    function()
    {
      return this._image.height;
    },
    function(val)
    {
    });
})

.addFunction("draw", function(ctx)
{
  var t = this.parent.Pos2D;
  ctx.save();
  ctx.translate(t.x, t.y);
  ctx.rotate(t.rotation)
  ctx.translate(-this.width / 2, -this.height / 2);
  ctx.drawImage(this._image, 0, 0);
  ctx.restore();
});<commit_msg>Image supports drawing sub-regions of an image<commit_after>TankJS.addComponent("Image")

.tags("Drawable")

.includes("Pos2D")

.initFunction(function()
{
  this.zdepth = 0;
  this.centered = true;
  this.subRectOrigin = undefined;
  this.subRectCorner = undefined;

  this._image = new Image();

  addProperty(this, "imagePath",
    function()
    {
      return this._image.src;
    },
    function(val)
    {
      this._image.src = val;
    });

  addProperty(this, "width",
    function()
    {
      if (!this.subRectOrigin)
        return this._image.width;
      return this.subRectCorner[0] - this.subRectOrigin[0];
    },
    function(val)
    {
      if (this.subRectOrigin)
        this.subRectCorner[0] = this.subRectOrigin[0] + val;
    });

  addProperty(this, "height",
    function()
    {
      if (!this.subRectOrigin)
        return this._image.height;
      return this.subRectCorner[1] - this.subRectOrigin[1];
    },
    function(val)
    {
      if (this.subRectOrigin)
        this.subRectCorner[1] = this.subRectOrigin[1] + val;
    });
})

.addFunction("draw", function(ctx)
{
  var t = this.parent.Pos2D;
  ctx.save();
  ctx.translate(t.x, t.y);
  ctx.rotate(t.rotation)
  if (this.centered)
    ctx.translate(-this.width / 2, -this.height / 2);

  if (this.subRectOrigin)
    ctx.drawImage(this._image, this.subRectOrigin[0], this.subRectOrigin[1], this.width, this.height, 0, 0, this.width, this.height);
  else
    ctx.drawImage(this._image, 0, 0);

  ctx.restore();
});<|endoftext|>


<commit_before>// ==UserScript==
// @id             ingress-intel-total-conversion@jonatkins
// @name           IITC: Ingress intel map total conversion
// @version        0.29.1.@@DATETIMEVERSION@@
// @namespace      https://github.com/IITC-CE/ingress-intel-total-conversion
// @updateURL      @@UPDATEURL@@
// @downloadURL    @@DOWNLOADURL@@
// @description    [@@BUILDNAME@@-@@BUILDDATE@@] Total conversion for the ingress intel map.
// @include        https://intel.ingress.com/*
// @match          https://intel.ingress.com/*
// @grant          none
// @run-at         document-end
// ==/UserScript==

// REPLACE ORIG SITE ///////////////////////////////////////////////////
if(document.getElementsByTagName('html')[0].getAttribute('itemscope') != null)
  throw('Ingress Intel Website is down, not a userscript issue.');
window.iitcBuildDate = '@@BUILDDATE@@';

// disable vanilla JS
window.onload = function() {};
document.body.onload = function() {};

//originally code here parsed the


<commit_before>import Individu from "@/lib/Individu"

export const createIndividuMixin = (props) => {
  const { fieldName = props, optional = false } = props

  return {
    data: function () {
      const entityName = this.$route.path.split("/")[2]
      const id = this.$route.params.id
      const role = id.split("_")[0]
      const value = this.$store.getters.getAnswer(id, entityName, fieldName)
      return {
        error: false,
        fieldName,
        id,
        value,
        role,
        optional,
      }
    },
    methods: {
      getLabel: function (type) {
        return Individu.label(this.individu, type)
      },
      requiredValueMissing: function () {
        const hasError = !this.optional && this.value === undefined
        this.$store.dispatch(
          "updateError",
          hasError && "Ce champ est obligatoire."
        )
        return hasError
      },
      onSubmit: function () {
        if (this.requiredValueMissing()) {
          return
        }
        this.$store.dispatch("answer", {
          id: this.$route.params.id,
          entityName: "individu",
          fieldName: this.fieldName,
          value: this.value,
        })
        this.$push()
      },
    },
  }
}
<commit_msg>Fix optional string value (#1887)<commit_after>import Individu from "@/lib/Individu"

export const createIndividuMixin = (props) => {
  const { fieldName = props, optional = false } = props

  return {
    data: function () {
      const entityName = this.$route.path.split("/")[2]
      const id = this.$route.params.id
      const role = id.split("_")[0]
      const value = this.$store.getters.getAnswer(id, entityName, fieldName)
      return {
        error: false,
        fieldName,
        id,
        value,
        role,
        optional,
      }
    },
    methods: {
      getLabel: function (type) {
        return Individu.label(this.individu, type)
      },
      requiredValueMissing: function () {
        const hasError =
          !this.optional && (this.value === undefined || this.value === "")
        this.$store.dispatch(
          "updateError",
          hasError && "Ce champ est obligatoire."
        )
        return hasError
      },
      onSubmit: function () {
        if (this.requiredValueMissing()) {
          return
        }
        this.$store.dispatch("answer", {
          id: this.$route.params.id,
          entityName: "individu",
          fieldName: this.fieldName,
          value: this.value,
        })
        this.$push()
      },
    },
  }
}
<|endoftext|>


<commit_before>/*! resol-vbus | Copyright (c) 2013-2017, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var moreints = require('buffer-more-ints');

var HeaderSet = require('./header-set');
var Packet = require('./packet');

var Converter = require('./converter');

var optionKeys = [
    'topologyScanOnly',
];

var VBusRecordingConverter = Converter.extend(/** @lends VBusRecordingConverter# */ {

    topologyScanOnly: false,

    rxBuffer: null,

    headerSet: null,

    headerSetTimestamp: null,

    currentChannel: 0,

    knownHeaderIds: null,

    /**
     * Creates a new VBusRecordingConverter instance.
     *
     * @constructs
     * @augments Converter
     *
     * @classdesc
     * This Converter subclass converts Header and HeaderSet instances to and from a binary stream that
     * conforms to the VBus Recording File Format (the binary file format used e.g. to store data on the
     * Datalogger devices).
     */
    constructor: function(options) {
        Converter.call(this, options);

        _.extend(this, _.pick(options, optionKeys));

        this.knownHeaderIds = {};
    },

    reset: function() {
        this.rxBuffer = null;
    },

    end: function() {
        var _this = this;

        if (this.objectMode) {
            // nop
        } else if (this.topologyScanOnly) {
            this._processBuffer(new Buffer(0), true, function(record) {
                _this._processRecordForTopologyScan(record);
            });

            this._constructTopologyHeaderSet();
        } else {
            this._processBuffer(new Buffer(0), true, function(record) {
                _this._processRecord(record);
            });
        }

        this._emitHeaderSet();

        return Converter.prototype.end.apply(this, arguments);
    },

    convertRawData: function(rawData) {
        if (this.objectMode) {
            return Converter.prototype.convertRawData.apply(this, arguments);
        } else {
            var buffers = [];

            var createBuffer = function(type, length, timestamp) {
                var buffer = new Buffer(length);
                buffer.fill(0);

                buffer [0] = 0xA5;
                buffer [1] = (type & 0x0F) | ((type & 0x0F) << 4);
                buffer.writeUInt16LE(length, 2);
                buffer.writeUInt16LE(length, 4);
                moreints.writeUInt64LE(buffer, timestamp.getTime(), 6);

                return buffer;
            };

            var buffer;
            if (rawData.channel !== this.currentChannel) {
                buffer = createBuffer(7, 16, new Date(0));
                buffer.writeUInt16LE(rawData.channel, 14);
                buffers.push(buffer);

                this.currentChannel = rawData.channel;
            }

            buffer = createBuffer(8, 22 + rawData.buffer.length, rawData.startTimestamp);
            moreints.writeUInt64LE(buffer, rawData.endTimestamp.getTime(), 14);
            rawData.buffer.copy(buffer, 22, 0, rawData.buffer.length);
            buffers.push(buffer);

            buffer = Buffer.concat(buffers);
            return this.push(buffer);
        }
    },

    convertHeader: function(header) {
        if (this.objectMode) {
            return Converter.prototype.convertHeader.apply(this, arguments);
        } else {
            return this._convertHeaders(header.timestamp, [ header ]);
        }
    },

    convertHeaderSet: function(headerSet) {
        if (this.objectMode) {
            return Converter.prototype.convertHeaderSet.apply(this, arguments);
        } else {
            return this._convertHeaders(headerSet.timestamp, headerSet.getSortedHeaders());
        }
    },

    _convertHeaders: function(timestamp, headers) {
        var buffers = [];

        var createBuffer = function(type, length, timestamp) {
            var buffer = new Buffer(length);
            buffer.fill(0);

            buffer [0] = 0xA5;
            buffer [1] = (type & 0x0F) | ((type & 0x0F) << 4);
            buffer.writeUInt16LE(length, 2);
            buffer.writeUInt16LE(length, 4);
            moreints.writeUInt64LE(buffer, timestamp.getTime(), 6);

            return buffer;
        };

        var buffer = createBuffer(4, 14, timestamp);
        buffers.push(buffer);

        var currentChannel = 0;

        _.forEach(headers, function(header) {
            var majorVersion = header.getProtocolVersion() >> 4;

            var dataLength;
            if (majorVersion === 1) {
                dataLength = header.frameCount * 4;
            } else {
                dataLength = -1;
            }

            if (dataLength >= 0) {
                var buffer;

                if (currentChannel !== header.channel) {
                    currentChannel = header.channel;

                    buffer = createBuffer(7, 16, new Date(0));
                    buffer.writeUInt16LE(header.channel, 14);
                    buffers.push(buffer);
                }

                buffer = createBuffer(6, 26 + dataLength, header.timestamp);

                buffer.writeUInt16LE(header.destinationAddress, 14);
                buffer.writeUInt16LE(header.sourceAddress, 16);
                buffer.writeUInt16LE(header.getProtocolVersion(), 18);

                if (majorVersion === 1) {
                    buffer.writeUInt16LE(header.command, 20);
                    buffer.writeUInt16LE(dataLength, 22);
                    buffer.writeUInt16LE(0, 24);

                    header.frameData.copy(buffer, 26, 0, dataLength);
                }

                buffers.push(buffer);
            }
        });

        buffer = Buffer.concat(buffers);
        return this.push(buffer);
    },

    _read: function() {
        // nop
    },

    _write: function(chunk, encoding, callback) {
        var _this = this;

        if (this.objectMode) {
            return Converter.prototype._write.apply(this, arguments);
        } else if (this.topologyScanOnly) {
            this._processBuffer(chunk, false, function(record) {
                _this._processRecordForTopologyScan(record);
            });
            callback();
        } else {
            this._processBuffer(chunk, false, function(record) {
                _this._processRecord(record);
            });
            callback();
        }
    },

    _processBuffer: function(chunk, endOfStream, processRecord) {
        var buffer;
        if (this.rxBuffer) {
            buffer = Buffer.concat([ this.rxBuffer, chunk ]);
        } else {
            buffer = chunk;
        }

        var getRecordLength = function(index) {
            var length;
            if (index > buffer.length - 6) {
                length = -1;
            } else if (buffer [index] !== 0xA5) {
                length = 0;
            } else if ((buffer [index + 1] >> 4) !== (buffer [index + 1] & 15)) {
                length = 0;
            } else if (buffer [index + 2] !== buffer [index + 4]) {
                length = 0;
            } else if (buffer [index + 3] !== buffer [index + 5]) {
                length = 0;
            } else {
                length = buffer.readUInt16LE(index + 2);

                if ((index + length) > buffer.length) {
                    length = -1;
                }
            }
            return length;
        };

        var currentIndex = 0, currentLength = getRecordLength(0), nextIndex, nextLength, start = 0;
        while ((currentLength >= 0) && (currentIndex < buffer.length)) {
            if (currentLength > 0) {
                nextIndex = currentIndex + currentLength;
            } else {
                nextIndex = currentIndex + 1;
            }

            nextLength = getRecordLength(nextIndex);

            if ((nextLength < 0) && !endOfStream) {
                break;
            }

            if ((currentLength > 0) && ((nextLength > 0) || (nextIndex === buffer.length))) {
                var record = buffer.slice(currentIndex, nextIndex);

                processRecord(record);

                start = nextIndex;
            } else if (nextIndex !== (currentIndex + 1)) {
                nextIndex = currentIndex + 1;
                nextLength = getRecordLength(nextIndex);

                if (nextLength < 0) {
                    break;
                }
            }

            currentIndex = nextIndex;
            currentLength = nextLength;
        }

        var maxLength = 65536;
        if (buffer.length - start >= maxLength) {
            start = buffer.length - maxLength;
        }

        if (start < buffer.length) {
            this.rxBuffer = new Buffer(buffer.slice(start));
        } else {
            this.rxBuffer = null;
        }
    },

    _processRecord: function(buffer) {
        var type = buffer [1] & 0x0F;
        var timestamp = moreints.readUInt64LE(buffer, 6);

        if (type === 3) {
            this._processType3Record(buffer, timestamp);
        } else if (type === 4) {
            this._emitHeaderSet();

            this.headerSet = new HeaderSet();

            this.headerSetTimestamp = new Date(timestamp);

            this.currentChannel = 0;
        } else if ((type === 6) && (buffer.length >= 20)) {
            var destinationAddress = buffer.readUInt16LE(14);
            var sourceAddress = buffer.readUInt16LE(16);
            var protocolVersion = buffer.readUInt16LE(18);

            var majorVersion = protocolVersion >> 4;
            if ((majorVersion === 1) && (buffer.length >= 26)) {
                var command = buffer.readUInt16LE(20);
                var dataLength = buffer.readUInt16LE(22);

                if (buffer.length >= 26 + dataLength) {
                    var frameCount = Math.floor(dataLength / 4);

                    var frameData = new Buffer(127 * 4);
                    buffer.copy(frameData, 0, 26, 26 + dataLength);

                    var header = new Packet({
                        timestamp: new Date(timestamp),
                        channel: this.currentChannel,
                        destinationAddress: destinationAddress,
                        sourceAddress: sourceAddress,
                        command: command,
                        frameCount: frameCount,
                        frameData: frameData,
                        dontCopyFrameData: true,
                    });

                    if (this.headerSet) {
                        this.headerSet.addHeader(header);
                    }

                    this.emit('header', header);
                }
            }
        } else if ((type === 7) && (buffer.length >= 16)) {
            this.currentChannel = buffer [14];
        } else if ((type === 8) && (buffer.length >= 22)) {
            var endTimestamp = moreints.readUInt64LE(buffer, 14);
            var rawBuffer = new Buffer(buffer.length - 22);
            buffer.copy(rawBuffer, 0, 22, buffer.length);

            this.emit('rawData', {
                startTimestamp: new Date(timestamp),
                endTimestamp: new Date(endTimestamp),
                channel: this.currentChannel,
                buffer: rawBuffer,
            });
        }
    },

    _processType3Record: function(buffer, timestamp) {
        var destinationAddress = buffer.readUInt16LE(14);
        var sourceAddress = buffer.readUInt16LE(16);
        var protocolVersion = buffer.readUInt16LE(18);

        var majorVersion = protocolVersion >> 4;
        if ((majorVersion === 1) && (buffer.length >= 26)) {
            var command = buffer.readUInt16LE(20);
            var dataLength = buffer.readUInt16LE(22);

            if (buffer.length >= 26 + dataLength) {
                var frameCount = Math.floor(dataLength / 4);

                var frameData = new Buffer(127 * 4);
                buffer.copy(frameData, 0, 26, 26 + dataLength);

                var header = new Packet({
                    timestamp: new Date(timestamp),
                    channel: this.currentChannel,
                    destinationAddress: destinationAddress,
                    sourceAddress: sourceAddress,
                    command: command,
                    frameCount: frameCount,
                    frameData: frameData,
                    dontCopyFrameData: true,
                });

                if (destinationAddress === 0x0010) {
                    this._emitHeaderSet();
                } else if (this.headerSet && this.headerSet.containsHeader(header)) {
                    this._emitHeaderSet();
                }

                if (!this.headerSet) {
                    this.headerSet = new HeaderSet();

                    this.headerSet.timestamp = header.timestamp;
                }

                this.headerSet.addHeader(header);

                this.emit('header', header);
            }
        }
    },

    _emitHeaderSet: function() {
        if (this.headerSet) {
            if (this.headerSetTimestamp) {
                this.headerSet.timestamp = this.headerSetTimestamp;
            }

            this.emit('headerSet', this.headerSet);

            this.headerSet = null;
        }
    },

    _processRecordForTopologyScan: function(buffer) {
        var type = buffer [1] & 0x0F;

        var destinationAddress = 0, sourceAddress = 0, protocolVersion = 0, command = 0, hasHeader = false;
        if (((type === 3) || (type === 6)) && (buffer.length >= 20)) {
            destinationAddress = buffer.readUInt16LE(14);
            sourceAddress = buffer.readUInt16LE(16);
            protocolVersion = buffer.readUInt16LE(18);

            var majorVersion = protocolVersion >> 4;
            if ((majorVersion === 1) && (buffer.length >= 26)) {
                command = buffer.readUInt16LE(20);
                hasHeader = true;
            }
        } else if (type === 4) {
            this.currentChannel = 0;
        } else if ((type === 7) && (buffer.length >= 16)) {
            this.currentChannel = buffer [14];
        } else if ((type === 8) && (buffer.length >= 22)) {
            var startTimestamp = moreints.readUInt64LE(buffer, 6);
            var endTimestamp = moreints.readUInt64LE(buffer, 14);
            var rawBuffer = new Buffer(buffer.length - 22);
            buffer.copy(rawBuffer, 0, 22, buffer.length);

            this.emit('rawData', {
                startTimestamp: new Date(startTimestamp),
                endTimestamp: new Date(endTimestamp),
                channel: this.currentChannel,
                buffer: rawBuffer,
            });
        }

        if (hasHeader) {
            var headerIdBuffer = new Buffer(8);
            headerIdBuffer [0] = this.currentChannel;
            headerIdBuffer.writeUInt16LE(destinationAddress, 1);
            headerIdBuffer.writeUInt16LE(sourceAddress, 3);
            headerIdBuffer [5] = protocolVersion;
            headerIdBuffer.writeUInt16LE(command, 6);

            var headerId = headerIdBuffer.toString('hex');

            this.knownHeaderIds [headerId] = true;
        }
    },

    _constructTopologyHeaderSet: function() {
        var headerSet = new HeaderSet();

        var timestamp = new Date(0);

        _.forEach(_.keys(this.knownHeaderIds), function(headerId) {
            var headerIdBuffer = new Buffer(headerId, 'hex');

            var channel = headerIdBuffer [0];
            var destinationAddress = headerIdBuffer.readUInt16LE(1);
            var sourceAddress = headerIdBuffer.readUInt16LE(3);
            var protocolVersion = headerIdBuffer [5];
            var command = headerIdBuffer.readUInt16LE(6);

            var majorVersion = (protocolVersion >> 4);
            if (majorVersion === 1) {
                var packet = new Packet({
                    timestamp: timestamp,
                    channel: channel,
                    destinationAddress: destinationAddress,
                    sourceAddress: sourceAddress,
                    command: command,
                    frameCount: 0,
                });

                headerSet.addHeader(packet);
            } else {
                throw new Error('Unsupported major version');
            }
        });

        headerSet.timestamp = timestamp;

        this.headerSet = headerSet;
    },

});

module.exports = VBusRecordingConverter;
<commit_msg>Added function to create type-9-records used for comments<commit_after>/*! resol-vbus | Copyright (c) 2013-2017, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var moreints = require('buffer-more-ints');

var HeaderSet = require('./header-set');
var Packet = require('./packet');

var Converter = require('./converter');

var optionKeys = [
    'topologyScanOnly',
];

var VBusRecordingConverter = Converter.extend(/** @lends VBusRecordingConverter# */ {

    topologyScanOnly: false,

    rxBuffer: null,

    headerSet: null,

    headerSetTimestamp: null,

    currentChannel: 0,

    knownHeaderIds: null,

    /**
     * Creates a new VBusRecordingConverter instance.
     *
     * @constructs
     * @augments Converter
     *
     * @classdesc
     * This Converter subclass converts Header and HeaderSet instances to and from a binary stream that
     * conforms to the VBus Recording File Format (the binary file format used e.g. to store data on the
     * Datalogger devices).
     */
    constructor: function(options) {
        Converter.call(this, options);

        _.extend(this, _.pick(options, optionKeys));

        this.knownHeaderIds = {};
    },

    reset: function() {
        this.rxBuffer = null;
    },

    end: function() {
        var _this = this;

        if (this.objectMode) {
            // nop
        } else if (this.topologyScanOnly) {
            this._processBuffer(new Buffer(0), true, function(record) {
                _this._processRecordForTopologyScan(record);
            });

            this._constructTopologyHeaderSet();
        } else {
            this._processBuffer(new Buffer(0), true, function(record) {
                _this._processRecord(record);
            });
        }

        this._emitHeaderSet();

        return Converter.prototype.end.apply(this, arguments);
    },

    convertRawData: function(rawData) {
        if (this.objectMode) {
            return Converter.prototype.convertRawData.apply(this, arguments);
        } else {
            var buffers = [];

            var createBuffer = function(type, length, timestamp) {
                var buffer = new Buffer(length);
                buffer.fill(0);

                buffer [0] = 0xA5;
                buffer [1] = (type & 0x0F) | ((type & 0x0F) << 4);
                buffer.writeUInt16LE(length, 2);
                buffer.writeUInt16LE(length, 4);
                moreints.writeUInt64LE(buffer, timestamp.getTime(), 6);

                return buffer;
            };

            var buffer;
            if (rawData.channel !== this.currentChannel) {
                buffer = createBuffer(7, 16, new Date(0));
                buffer.writeUInt16LE(rawData.channel, 14);
                buffers.push(buffer);

                this.currentChannel = rawData.channel;
            }

            buffer = createBuffer(8, 22 + rawData.buffer.length, rawData.startTimestamp);
            moreints.writeUInt64LE(buffer, rawData.endTimestamp.getTime(), 14);
            rawData.buffer.copy(buffer, 22, 0, rawData.buffer.length);
            buffers.push(buffer);

            buffer = Buffer.concat(buffers);
            return this.push(buffer);
        }
    },

    convertComment: function(timestamp, comment) {
        if (this.objectMode) {
            return Converter.prototype.convertRawData.apply(this, arguments);
        } else {
            var buffers = [];

            var createBuffer = function(type, length, timestamp) {
                var buffer = new Buffer(length);
                buffer.fill(0);

                buffer [0] = 0xA5;
                buffer [1] = (type & 0x0F) | ((type & 0x0F) << 4);
                buffer.writeUInt16LE(length, 2);
                buffer.writeUInt16LE(length, 4);
                moreints.writeUInt64LE(buffer, timestamp.getTime(), 6);

                return buffer;
            };

            const rawData = Buffer.from(comment.toString());

            var buffer;
            buffer = createBuffer(9, 14 + rawData.length, timestamp);
            rawData.copy(buffer, 14, 0, rawData.length);
            buffers.push(buffer);

            buffer = Buffer.concat(buffers);
            return this.push(buffer);
        }
    },

    convertHeader: function(header) {
        if (this.objectMode) {
            return Converter.prototype.convertHeader.apply(this, arguments);
        } else {
            return this._convertHeaders(header.timestamp, [ header ]);
        }
    },

    convertHeaderSet: function(headerSet) {
        if (this.objectMode) {
            return Converter.prototype.convertHeaderSet.apply(this, arguments);
        } else {
            return this._convertHeaders(headerSet.timestamp, headerSet.getSortedHeaders());
        }
    },

    _convertHeaders: function(timestamp, headers) {
        var buffers = [];

        var createBuffer = function(type, length, timestamp) {
            var buffer = new Buffer(length);
            buffer.fill(0);

            buffer [0] = 0xA5;
            buffer [1] = (type & 0x0F) | ((type & 0x0F) << 4);
            buffer.writeUInt16LE(length, 2);
            buffer.writeUInt16LE(length, 4);
            moreints.writeUInt64LE(buffer, timestamp.getTime(), 6);

            return buffer;
        };

        var buffer = createBuffer(4, 14, timestamp);
        buffers.push(buffer);

        var currentChannel = 0;

        _.forEach(headers, function(header) {
            var majorVersion = header.getProtocolVersion() >> 4;

            var dataLength;
            if (majorVersion === 1) {
                dataLength = header.frameCount * 4;
            } else {
                dataLength = -1;
            }

            if (dataLength >= 0) {
                var buffer;

                if (currentChannel !== header.channel) {
                    currentChannel = header.channel;

                    buffer = createBuffer(7, 16, new Date(0));
                    buffer.writeUInt16LE(header.channel, 14);
                    buffers.push(buffer);
                }

                buffer = createBuffer(6, 26 + dataLength, header.timestamp);

                buffer.writeUInt16LE(header.destinationAddress, 14);
                buffer.writeUInt16LE(header.sourceAddress, 16);
                buffer.writeUInt16LE(header.getProtocolVersion(), 18);

                if (majorVersion === 1) {
                    buffer.writeUInt16LE(header.command, 20);
                    buffer.writeUInt16LE(dataLength, 22);
                    buffer.writeUInt16LE(0, 24);

                    header.frameData.copy(buffer, 26, 0, dataLength);
                }

                buffers.push(buffer);
            }
        });

        buffer = Buffer.concat(buffers);
        return this.push(buffer);
    },

    _read: function() {
        // nop
    },

    _write: function(chunk, encoding, callback) {
        var _this = this;

        if (this.objectMode) {
            return Converter.prototype._write.apply(this, arguments);
        } else if (this.topologyScanOnly) {
            this._processBuffer(chunk, false, function(record) {
                _this._processRecordForTopologyScan(record);
            });
            callback();
        } else {
            this._processBuffer(chunk, false, function(record) {
                _this._processRecord(record);
            });
            callback();
        }
    },

    _processBuffer: function(chunk, endOfStream, processRecord) {
        var buffer;
        if (this.rxBuffer) {
            buffer = Buffer.concat([ this.rxBuffer, chunk ]);
        } else {
            buffer = chunk;
        }

        var getRecordLength = function(index) {
            var length;
            if (index > buffer.length - 6) {
                length = -1;
            } else if (buffer [index] !== 0xA5) {
                length = 0;
            } else if ((buffer [index + 1] >> 4) !== (buffer [index + 1] & 15)) {
                length = 0;
            } else if (buffer [index + 2] !== buffer [index + 4]) {
                length = 0;
            } else if (buffer [index + 3] !== buffer [index + 5]) {
                length = 0;
            } else {
                length = buffer.readUInt16LE(index + 2);

                if ((index + length) > buffer.length) {
                    length = -1;
                }
            }
            return length;
        };

        var currentIndex = 0, currentLength = getRecordLength(0), nextIndex, nextLength, start = 0;
        while ((currentLength >= 0) && (currentIndex < buffer.length)) {
            if (currentLength > 0) {
                nextIndex = currentIndex + currentLength;
            } else {
                nextIndex = currentIndex + 1;
            }

            nextLength = getRecordLength(nextIndex);

            if ((nextLength < 0) && !endOfStream) {
                break;
            }

            if ((currentLength > 0) && ((nextLength > 0) || (nextIndex === buffer.length))) {
                var record = buffer.slice(currentIndex, nextIndex);

                processRecord(record);

                start = nextIndex;
            } else if (nextIndex !== (currentIndex + 1)) {
                nextIndex = currentIndex + 1;
                nextLength = getRecordLength(nextIndex);

                if (nextLength < 0) {
                    break;
                }
            }

            currentIndex = nextIndex;
            currentLength = nextLength;
        }

        var maxLength = 65536;
        if (buffer.length - start >= maxLength) {
            start = buffer.length - maxLength;
        }

        if (start < buffer.length) {
            this.rxBuffer = new Buffer(buffer.slice(start));
        } else {
            this.rxBuffer = null;
        }
    },

    _processRecord: function(buffer) {
        var type = buffer [1] & 0x0F;
        var timestamp = moreints.readUInt64LE(buffer, 6);

        if (type === 3) {
            this._processType3Record(buffer, timestamp);
        } else if (type === 4) {
            this._emitHeaderSet();

            this.headerSet = new HeaderSet();

            this.headerSetTimestamp = new Date(timestamp);

            this.currentChannel = 0;
        } else if ((type === 6) && (buffer.length >= 20)) {
            var destinationAddress = buffer.readUInt16LE(14);
            var sourceAddress = buffer.readUInt16LE(16);
            var protocolVersion = buffer.readUInt16LE(18);

            var majorVersion = protocolVersion >> 4;
            if ((majorVersion === 1) && (buffer.length >= 26)) {
                var command = buffer.readUInt16LE(20);
                var dataLength = buffer.readUInt16LE(22);

                if (buffer.length >= 26 + dataLength) {
                    var frameCount = Math.floor(dataLength / 4);

                    var frameData = new Buffer(127 * 4);
                    buffer.copy(frameData, 0, 26, 26 + dataLength);

                    var header = new Packet({
                        timestamp: new Date(timestamp),
                        channel: this.currentChannel,
                        destinationAddress: destinationAddress,
                        sourceAddress: sourceAddress,
                        command: command,
                        frameCount: frameCount,
                        frameData: frameData,
                        dontCopyFrameData: true,
                    });

                    if (this.headerSet) {
                        this.headerSet.addHeader(header);
                    }

                    this.emit('header', header);
                }
            }
        } else if ((type === 7) && (buffer.length >= 16)) {
            this.currentChannel = buffer [14];
        } else if ((type === 8) && (buffer.length >= 22)) {
            var endTimestamp = moreints.readUInt64LE(buffer, 14);
            var rawBuffer = new Buffer(buffer.length - 22);
            buffer.copy(rawBuffer, 0, 22, buffer.length);

            this.emit('rawData', {
                startTimestamp: new Date(timestamp),
                endTimestamp: new Date(endTimestamp),
                channel: this.currentChannel,
                buffer: rawBuffer,
            });
        }
    },

    _processType3Record: function(buffer, timestamp) {
        var destinationAddress = buffer.readUInt16LE(14);
        var sourceAddress = buffer.readUInt16LE(16);
        var protocolVersion = buffer.readUInt16LE(18);

        var majorVersion = protocolVersion >> 4;
        if ((majorVersion === 1) && (buffer.length >= 26)) {
            var command = buffer.readUInt16LE(20);
            var dataLength = buffer.readUInt16LE(22);

            if (buffer.length >= 26 + dataLength) {
                var frameCount = Math.floor(dataLength / 4);

                var frameData = new Buffer(127 * 4);
                buffer.copy(frameData, 0, 26, 26 + dataLength);

                var header = new Packet({
                    timestamp: new Date(timestamp),
                    channel: this.currentChannel,
                    destinationAddress: destinationAddress,
                    sourceAddress: sourceAddress,
                    command: command,
                    frameCount: frameCount,
                    frameData: frameData,
                    dontCopyFrameData: true,
                });

                if (destinationAddress === 0x0010) {
                    this._emitHeaderSet();
                } else if (this.headerSet && this.headerSet.containsHeader(header)) {
                    this._emitHeaderSet();
                }

                if (!this.headerSet) {
                    this.headerSet = new HeaderSet();

                    this.headerSet.timestamp = header.timestamp;
                }

                this.headerSet.addHeader(header);

                this.emit('header', header);
            }
        }
    },

    _emitHeaderSet: function() {
        if (this.headerSet) {
            if (this.headerSetTimestamp) {
                this.headerSet.timestamp = this.headerSetTimestamp;
            }

            this.emit('headerSet', this.headerSet);

            this.headerSet = null;
        }
    },

    _processRecordForTopologyScan: function(buffer) {
        var type = buffer [1] & 0x0F;

        var destinationAddress = 0, sourceAddress = 0, protocolVersion = 0, command = 0, hasHeader = false;
        if (((type === 3) || (type === 6)) && (buffer.length >= 20)) {
            destinationAddress = buffer.readUInt16LE(14);
            sourceAddress = buffer.readUInt16LE(16);
            protocolVersion = buffer.readUInt16LE(18);

            var majorVersion = protocolVersion >> 4;
            if ((majorVersion === 1) && (buffer.length >= 26)) {
                command = buffer.readUInt16LE(20);
                hasHeader = true;
            }
        } else if (type === 4) {
            this.currentChannel = 0;
        } else if ((type === 7) && (buffer.length >= 16)) {
            this.currentChannel = buffer [14];
        } else if ((type === 8) && (buffer.length >= 22)) {
            var startTimestamp = moreints.readUInt64LE(buffer, 6);
            var endTimestamp = moreints.readUInt64LE(buffer, 14);
            var rawBuffer = new Buffer(buffer.length - 22);
            buffer.copy(rawBuffer, 0, 22, buffer.length);

            this.emit('rawData', {
                startTimestamp: new Date(startTimestamp),
                endTimestamp: new Date(endTimestamp),
                channel: this.currentChannel,
                buffer: rawBuffer,
            });
        }

        if (hasHeader) {
            var headerIdBuffer = new Buffer(8);
            headerIdBuffer [0] = this.currentChannel;
            headerIdBuffer.writeUInt16LE(destinationAddress, 1);
            headerIdBuffer.writeUInt16LE(sourceAddress, 3);
            headerIdBuffer [5] = protocolVersion;
            headerIdBuffer.writeUInt16LE(command, 6);

            var headerId = headerIdBuffer.toString('hex');

            this.knownHeaderIds [headerId] = true;
        }
    },

    _constructTopologyHeaderSet: function() {
        var headerSet = new HeaderSet();

        var timestamp = new Date(0);

        _.forEach(_.keys(this.knownHeaderIds), function(headerId) {
            var headerIdBuffer = new Buffer(headerId, 'hex');

            var channel = headerIdBuffer [0];
            var destinationAddress = headerIdBuffer.readUInt16LE(1);
            var sourceAddress = headerIdBuffer.readUInt16LE(3);
            var protocolVersion = headerIdBuffer [5];
            var command = headerIdBuffer.readUInt16LE(6);

            var majorVersion = (protocolVersion >> 4);
            if (majorVersion === 1) {
                var packet = new Packet({
                    timestamp: timestamp,
                    channel: channel,
                    destinationAddress: destinationAddress,
                    sourceAddress: sourceAddress,
                    command: command,
                    frameCount: 0,
                });

                headerSet.addHeader(packet);
            } else {
                throw new Error('Unsupported major version');
            }
        });

        headerSet.timestamp = timestamp;

        this.headerSet = headerSet;
    },

});

module.exports = VBusRecordingConverter;
<|endoftext|>


<commit_before>window.addEventListener('load', onLoad, false);

function onLoad() {
    SFX.createContext();

    SFX.loadSound('test.mp3', function(buffer) {
        var source = SFX.createSource(buffer);

        SFX.playSound(source, {
            volume: 0.2,

            onComplete: function() {
                console.log("done");
            }
        });
    });
}

//Temp namespace so we can easily refactor later when we come up with a name.
var SFX = {
    createContext: function() {
        window.AudioContext = window.AudioContext||window.webkitAudioContext;
        SFX.context = new AudioContext();
    },

    loadSound: function(url, onLoad) {
        var request = new XMLHttpRequest();
        request.open('GET', url, true);
        request.responseType = 'arraybuffer';

        // Decode asynchronously
        request.onload = function() {
            SFX.context.decodeAudioData(request.response, function(buffer) {
                if (onLoad) {
                    onLoad(buffer);
                }
            }, function(e) {
                console.error(e);
            });
        }

        request.send();
    },

    createSource: function(buffer) {
        var source = SFX.context.createBufferSource();
        source.buffer = buffer;
        return source;
    },

    playSound: function(source, opt) {
        var gainNode = SFX.context.createGain();

        var loop = false;
        var delay = 0;
        var volume = 1.0;

        if (opt) {
            loop = opt.loop || loop;
            delay = opt.delay || delay;
            volume = opt.volume || volume;
        }

        source.loop = loop || false;
        gainNode.gain.value = volume;

        //Connect the source to the gain node and connect the gain node to the destination.
        //Source -> Gain -> Destination
        source.connect(gainNode);
        gainNode.connect(SFX.context.destination);

        source.start(delay || 0);

        setTimeout(function() {
            if (opt && opt.onComplete) {
                opt.onComplete();
            }
        }, (source.buffer.duration + delay) * 1000);
    },

    stopSound: function(source, delay) {
        source.stop(delay);
    }
}
<commit_msg>Added master gain and other minor stuff.<commit_after>window.addEventListener('load', onLoad, false);

function onLoad() {
    SFX.createContext();

    SFX.loadSound('test.mp3', function(buffer) {
        var source = SFX.createSource(buffer);

        SFX.playSound(source, {
            gain: 0.1,

            onComplete: function() {
                console.log("done");
            }
        });
    });
}

//Temp namespace so we can easily refactor later when we come up with a name.
var SFX = {
    master: {}, //Global or master effect nodes.

    createContext: function() {
        window.AudioContext = window.AudioContext||window.webkitAudioContext;
        SFX.context = new AudioContext();

        SFX.master.gainNode = SFX.context.createGain();
        SFX.master.gainNode.connect(SFX.context.destination);
    },

    loadSound: function(url, onLoad) {
        var request = new XMLHttpRequest();
        request.open('GET', url, true);
        request.responseType = 'arraybuffer';

        // Decode asynchronously
        request.onload = function() {
            SFX.context.decodeAudioData(request.response, function(buffer) {
                if (onLoad) {
                    onLoad(buffer);
                }
            }, function(e) {
                console.error(e);
            });
        }

        request.send();
    },

    createSource: function(buffer) {
        var source = SFX.context.createBufferSource();
        source.buffer = buffer;
        return source;
    },

    playSound: function(source, opt) {
        var gainNode = SFX.context.createGain();

        var loop = false;
        var delay = 0;
        var gain = 1.0;

        if (opt) {
            loop = opt.loop || loop;
            delay = opt.delay || delay;
            gain = opt.gain || gain;
        }

        source.loop = loop;
        gainNode.gain.value = gain;

        //Connect the source to the gain node and connect the gain node to the destination.
        //Source -> Gain -> Master Gain -> Destination
        source.connect(gainNode);
        gainNode.connect(SFX.master.gainNode);

        source.start(delay);

        setTimeout(function() {
            if (opt && opt.onComplete) {
                opt.onComplete();
            }
        }, (source.buffer.duration + delay) * 1000);
    },

    stopSound: function(source, delay) {
        source.stop(delay);
    }
}
<|endoftext|>