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

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

let redis = require('redis');
let rclient = redis.createClient();

let Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let DNSManager = require('./DNSManager.js');
let dnsManager = new DNSManager('info');

let async2 = require('async');

let util = require('util');

let IntelTool = require('../net2/IntelTool');
let intelTool = new IntelTool();

let DestIPFoundHook = require('../hook/DestIPFoundHook');
let destIPFoundHook = new DestIPFoundHook();

let country = require('../extension/country/country.js');

const MAX_RECENT_INTERVAL = 24 * 60 * 60; // one day
const QUERY_MAX_FLOW = 10000;
const MAX_RECENT_FLOW = 50;
const MAX_CONCURRENT_ACTIVITY = 10;

let instance = null;
class FlowTool {
  constructor() {
    if(!instance) {
      instance = this;
    }
    return instance;
  }

  trimFlow(flow) {
    if(!flow)
      return;

    if("flows" in flow)
      delete flow.flows;

    if("pf" in flow)
      delete flow.pf;

    if("bl" in flow)
      delete flow.bl;

    if("af" in flow)
      delete flow.af;

    if("f" in flow)
      delete flow.f;

  }

  _mergeFlow(targetFlow, flow) {
    targetFlow.rb += flow.rb;
    targetFlow.ct += flow.ct;
    targetFlow.ob += flow.ob;
    targetFlow.du += flow.du;
    if (targetFlow.ts < flow.ts) {
      targetFlow.ts = flow.ts;
    }
    if (flow.pf) {
      for (let k in flow.pf) {
        if (targetFlow.pf[k] != null) {
          targetFlow.pf[k].rb += flow.pf[k].rb;
          targetFlow.pf[k].ob += flow.pf[k].ob;
          targetFlow.pf[k].ct += flow.pf[k].ct;
        } else {
          targetFlow.pf[k] = flow.pf[k]
        }
      }
    }

    if (flow.flows) {
      if (targetFlow.flows) {
        targetFlow.flows = targetFlow.flows.concat(flow.flows);
      } else {
        targetFlow.flows = flow.flows;
      }
    }
  }

  _getKey(flow) {
    let key = "";
    if (flow.sh === flow.lh) {
      key = flow.dh + ":" + flow.fd;
    } else {
      key = flow.sh + ":" + flow.fd;
    }
    return key;
  }

  _getRemoteIP(flow) {
    if (flow.sh === flow.lh) {
      return flow.dh;
    } else {
      return flow.sh;
    }
  }

  // append to existing flow or create new
  _appendFlow(conndb, flowObject, ip) {
    let o = flowObject;

    let key = this._getKey(o, ip);

    let flow = conndb[key];
    if (flow == null) {
      conndb[key] = o;
    } else {
      this._mergeFlow(flow, o);
    }
  }

  _flowStringToJSON(flow) {
    try {
      return JSON.parse(flow);
    } catch(err) {
      return null;
    }
  }

  _isFlowValid(flow) {
    let o = flow;

    if (!o) {
      log.error("Host:Flows:Sorting:Parsing", flow);
      return false;
    }
    if ( !o.rb || !o.ob ) {
      return false
    }
    if (o.rb === 0 && o.ob === 0) {
      // ignore zero length flows
      return false;
    }

    return true;
  }

  _enrichCountryInfo(flow) {
    let sh = flow.sh;
    let dh = flow.dh;
    let lh = flow.lh;

    if (sh === lh) {
      flow.country = country.getCountry(dh);
    } else {
      flow.country = country.getCountry(sh);
    }
  }

  // FIXME: support dynamically load intel from cloud
  _enrichDNSInfo(flows) {

    return new Promise((resolve, reject) => {
      async.eachLimit(flows, MAX_CONCURRENT_ACTIVITY, (flow, cb) => {
        let ip = this._getRemoteIP(flow);

        dnsManager.resolvehost(ip, (err, info, dnsData) => {
          if (err) {
            cb(err);
            return;
          }

          if (info && info.name) {
            flow.dhname = info.name;
          }

          cb();
        });
      }, (err) => {
        if(err) {
          reject(err);
          return;
        }

        resolve(flows);
      });
    });

  }

  prepareRecentFlowsForHost(json, listip) {
    if (!("flows" in json)) {
      json.flows = {};
    }

    json.flows.time = [];

    let promises = listip.map((ip) => {
      return this.getRecentOutgoingConnections(ip)
        .then((flows) => {
          Array.prototype.push.apply(json.flows.time, flows);
          return json;
        })
    });

    return Promise.all(promises);

  }

  _mergeFlows(flowObjects) {
    let mergedFlowObjects = [];
    let lastFlowObject = null;

    flowObjects.forEach((flowObject) => {
      if(!lastFlowObject) {
        mergedFlowObjects.push(flowObject);
        lastFlowObject = flowObject;
        return;
      }

      if (this._getKey(lastFlowObject) === this._getKey(flowObject)) {
        this._mergeFlow(lastFlowObject, flowObject);
      } else {
        mergedFlowObjects.push(flowObject);
        lastFlowObject = flowObject;
      }
    });

    return mergedFlowObjects;
  }

  // convert flow json to a simplified json format that's more readable by app
  toSimpleFlow(flow) {
    let f = {};

    f.ts = flow.ts;
    f.fd = flow.fd;

    if(flow.lh === flow.sh) {
      f.ip = flow.dh;
      f.upload = flow.ob;
      f.download = flow.rb;
    } else {
      f.ip = flow.sh;
      f.upload = flow.rb;
      f.download = flow.ob;
    }

    return f;
  }

  legacyGetRecentOutgoingConnections(ip) {

     let key = "flow:conn:in:" + ip;
     let to = new Date() / 1000;
     let from = to - MAX_RECENT_INTERVAL;

     return rclient.zrevrangebyscoreAsync([key, to, from, "LIMIT", 0 , MAX_RECENT_FLOW])
       .then((results) => {

         if(results === null || results.length === 0)
           return [];

         let flowObjects = results
           .map((x) => this._flowStringToJSON(x))
           .filter((x) => this._isFlowValid(x));

         flowObjects.forEach((x) => this.trimFlow(x));

         let mergedFlowObjects = [];
         let lastFlowObject = null;

         flowObjects.forEach((flowObject) => {
           if(!lastFlowObject) {
             mergedFlowObjects.push(flowObject);
             lastFlowObject = flowObject;
             return;
           }

           if (this._getKey(lastFlowObject) === this._getKey(flowObject)) {
             this._mergeFlow(lastFlowObject, flowObject);
           } else {
             mergedFlowObjects.push(flowObject);
             lastFlowObject = flowObject;
           }
         });

         // add country info
         mergedFlowObjects.forEach(this._enrichCountryInfo);

         return this._enrichDNSInfo(mergedFlowObjects);

       }).catch((err) => {
         log.error("Failed to query flow data for ip", ip, ":", err, err.stack, {});
       });
   }

  getRecentOutgoingConnections(ip) {
    return this.getRecentConnections(ip, "in")
  }

  getRecentIncomingConnections(ip) {
    return this.getRecentConnections(ip, "out");
  }

  getRecentConnections(ip, direction) {
    let key = util.format("flow:conn:%s:%s", direction, ip);
    let to = new Date() / 1000;
    let from = to - MAX_RECENT_INTERVAL;

    return async(() => {
      let results = await (rclient.zrevrangebyscoreAsync([key, to, from, "LIMIT", 0 , MAX_RECENT_FLOW]));

      if(results === null || results.length === 0)
        return [];

      let flowObjects = results
        .map((x) => this._flowStringToJSON(x))
        .filter((x) => this._isFlowValid(x));

      flowObjects.forEach((x) => this.trimFlow(x));

      let mergedFlow = this._mergeFlows(flowObjects);

      let simpleFlows = mergedFlow.map((f) => this.toSimpleFlow(f));

      let promises = Promise.all(simpleFlows.map((f) => {
        return intelTool.getIntel(f.ip)
        .then((intel) => {
          if(intel) {
            f.country = intel.country;
            f.host = intel.host;
            return f;
          } else {
            // intel not exists in redis, create a new one
            return async(() => {
              intel = await (destIPFoundHook.processIP(f.ip));
              f.country = intel.country;
              f.host = intel.host;
              return f;
            })();
          }
          return f;
        });
      }));

      return promises;
    })();
  }

  getFlowKey(ip, type) {
    return util.format("flow:conn:%s:%s", type, ip);
  }
  addFlow(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return rclient.zaddAsync(key, flow.ts, JSON.stringify(flow));
  }

  removeFlow(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return rclient.zremAsync(key, JSON.stringify(flow))
  }

  flowExists(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return async(() => {
      let result = await(rclient.zscoreAsync(key, JSON.stringify(flow)));

      if(result == null) {
        return false;
      } else {
        return true;
      }
    })();
  }

  queryFlows(ip, type, begin, end) {
    let key = this.getFlowKey(ip, type);

    return rclient.zrangebyscoreAsync(key, "(" + begin, end) // char '(' means open interval
      .then((flowStrings) => {
        return flowStrings.map((flowString) => JSON.parse(flowString));
      })
  }

  getDestIP(flow) {
    if(flow.lh === flow.sh) {
      return flow.dh;
    } else {
      return flow.sh;
    }
  }

  getDownloadTraffic(flow) {
    if(flow.lh === flow.sh) {
      return flow.rb;
    } else {
      return flow.ob;
    }
  }

  getUploadTraffic(flow) {
    if(flow.lh === flow.sh) {
      return flow.ob;
    } else {
      return flow.rb;
    }
  }
}

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

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

let redis = require('redis');
let rclient = redis.createClient();

let Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let DNSManager = require('./DNSManager.js');
let dnsManager = new DNSManager('info');

let async2 = require('async');

let util = require('util');

let IntelTool = require('../net2/IntelTool');
let intelTool = new IntelTool();

let DestIPFoundHook = require('../hook/DestIPFoundHook');
let destIPFoundHook = new DestIPFoundHook();

let country = require('../extension/country/country.js');

const MAX_RECENT_INTERVAL = 24 * 60 * 60; // one day
const QUERY_MAX_FLOW = 10000;
const MAX_RECENT_FLOW = 50;
const MAX_CONCURRENT_ACTIVITY = 10;

let instance = null;
class FlowTool {
  constructor() {
    if(!instance) {
      instance = this;
    }
    return instance;
  }

  trimFlow(flow) {
    if(!flow)
      return;

    if("flows" in flow)
      delete flow.flows;

    if("pf" in flow)
      delete flow.pf;

    if("bl" in flow)
      delete flow.bl;

    if("af" in flow)
      delete flow.af;

    if("f" in flow)
      delete flow.f;

  }

  _mergeFlow(targetFlow, flow) {
    targetFlow.rb += flow.rb;
    targetFlow.ct += flow.ct;
    targetFlow.ob += flow.ob;
    targetFlow.du += flow.du;
    if (targetFlow.ts < flow.ts) {
      targetFlow.ts = flow.ts;
    }
    if (flow.pf) {
      for (let k in flow.pf) {
        if (targetFlow.pf[k] != null) {
          targetFlow.pf[k].rb += flow.pf[k].rb;
          targetFlow.pf[k].ob += flow.pf[k].ob;
          targetFlow.pf[k].ct += flow.pf[k].ct;
        } else {
          targetFlow.pf[k] = flow.pf[k]
        }
      }
    }

    if (flow.flows) {
      if (targetFlow.flows) {
        targetFlow.flows = targetFlow.flows.concat(flow.flows);
      } else {
        targetFlow.flows = flow.flows;
      }
    }
  }

  _getKey(flow) {
    let key = "";
    if (flow.sh === flow.lh) {
      key = flow.dh + ":" + flow.fd;
    } else {
      key = flow.sh + ":" + flow.fd;
    }
    return key;
  }

  _getRemoteIP(flow) {
    if (flow.sh === flow.lh) {
      return flow.dh;
    } else {
      return flow.sh;
    }
  }

  // append to existing flow or create new
  _appendFlow(conndb, flowObject, ip) {
    let o = flowObject;

    let key = this._getKey(o, ip);

    let flow = conndb[key];
    if (flow == null) {
      conndb[key] = o;
    } else {
      this._mergeFlow(flow, o);
    }
  }

  _flowStringToJSON(flow) {
    try {
      return JSON.parse(flow);
    } catch(err) {
      return null;
    }
  }

  _isFlowValid(flow) {
    let o = flow;

    if (!o) {
      log.error("Host:Flows:Sorting:Parsing", flow);
      return false;
    }
    if ( !o.rb || !o.ob ) {
      return false
    }
    if (o.rb === 0 && o.ob === 0) {
      // ignore zero length flows
      return false;
    }

    return true;
  }

  _enrichCountryInfo(flow) {
    let sh = flow.sh;
    let dh = flow.dh;
    let lh = flow.lh;

    if (sh === lh) {
      flow.country = country.getCountry(dh);
    } else {
      flow.country = country.getCountry(sh);
    }
  }

  // FIXME: support dynamically load intel from cloud
  _enrichDNSInfo(flows) {

    return new Promise((resolve, reject) => {
      async.eachLimit(flows, MAX_CONCURRENT_ACTIVITY, (flow, cb) => {
        let ip = this._getRemoteIP(flow);

        dnsManager.resolvehost(ip, (err, info, dnsData) => {
          if (err) {
            cb(err);
            return;
          }

          if (info && info.name) {
            flow.dhname = info.name;
          }

          cb();
        });
      }, (err) => {
        if(err) {
          reject(err);
          return;
        }

        resolve(flows);
      });
    });

  }

  prepareRecentFlowsForHost(json, listip) {
    if (!("flows" in json)) {
      json.flows = {};
    }

    json.flows.recent = [];

    let promises = listip.map((ip) => {
      return this.getRecentOutgoingConnections(ip)
        .then((flows) => {
          Array.prototype.push.apply(json.flows.recent, flows);
          return json;
        })
    });

    return Promise.all(promises);

  }

  _mergeFlows(flowObjects) {
    let mergedFlowObjects = [];
    let lastFlowObject = null;

    flowObjects.forEach((flowObject) => {
      if(!lastFlowObject) {
        mergedFlowObjects.push(flowObject);
        lastFlowObject = flowObject;
        return;
      }

      if (this._getKey(lastFlowObject) === this._getKey(flowObject)) {
        this._mergeFlow(lastFlowObject, flowObject);
      } else {
        mergedFlowObjects.push(flowObject);
        lastFlowObject = flowObject;
      }
    });

    return mergedFlowObjects;
  }

  // convert flow json to a simplified json format that's more readable by app
  toSimpleFlow(flow) {
    let f = {};

    f.ts = flow.ts;
    f.fd = flow.fd;

    if(flow.lh === flow.sh) {
      f.ip = flow.dh;
      f.upload = flow.ob;
      f.download = flow.rb;
    } else {
      f.ip = flow.sh;
      f.upload = flow.rb;
      f.download = flow.ob;
    }

    return f;
  }

  legacyGetRecentOutgoingConnections(ip) {

     let key = "flow:conn:in:" + ip;
     let to = new Date() / 1000;
     let from = to - MAX_RECENT_INTERVAL;

     return rclient.zrevrangebyscoreAsync([key, to, from, "LIMIT", 0 , MAX_RECENT_FLOW])
       .then((results) => {

         if(results === null || results.length === 0)
           return [];

         let flowObjects = results
           .map((x) => this._flowStringToJSON(x))
           .filter((x) => this._isFlowValid(x));

         flowObjects.forEach((x) => this.trimFlow(x));

         let mergedFlowObjects = [];
         let lastFlowObject = null;

         flowObjects.forEach((flowObject) => {
           if(!lastFlowObject) {
             mergedFlowObjects.push(flowObject);
             lastFlowObject = flowObject;
             return;
           }

           if (this._getKey(lastFlowObject) === this._getKey(flowObject)) {
             this._mergeFlow(lastFlowObject, flowObject);
           } else {
             mergedFlowObjects.push(flowObject);
             lastFlowObject = flowObject;
           }
         });

         // add country info
         mergedFlowObjects.forEach(this._enrichCountryInfo);

         return this._enrichDNSInfo(mergedFlowObjects);

       }).catch((err) => {
         log.error("Failed to query flow data for ip", ip, ":", err, err.stack, {});
       });
   }

  getRecentOutgoingConnections(ip) {
    return this.getRecentConnections(ip, "in")
  }

  getRecentIncomingConnections(ip) {
    return this.getRecentConnections(ip, "out");
  }

  getRecentConnections(ip, direction) {
    let key = util.format("flow:conn:%s:%s", direction, ip);
    let to = new Date() / 1000;
    let from = to - MAX_RECENT_INTERVAL;

    return async(() => {
      let results = await (rclient.zrevrangebyscoreAsync([key, to, from, "LIMIT", 0 , MAX_RECENT_FLOW]));

      if(results === null || results.length === 0)
        return [];

      let flowObjects = results
        .map((x) => this._flowStringToJSON(x))
        .filter((x) => this._isFlowValid(x));

      flowObjects.forEach((x) => this.trimFlow(x));

      let mergedFlow = this._mergeFlows(flowObjects);

      let simpleFlows = mergedFlow.map((f) => this.toSimpleFlow(f));

      let promises = Promise.all(simpleFlows.map((f) => {
        return intelTool.getIntel(f.ip)
        .then((intel) => {
          if(intel) {
            f.country = intel.country;
            f.host = intel.host;
            return f;
          } else {
            // intel not exists in redis, create a new one
            return async(() => {
              intel = await (destIPFoundHook.processIP(f.ip));
              f.country = intel.country;
              f.host = intel.host;
              return f;
            })();
          }
          return f;
        });
      }));

      return promises;
    })();
  }

  getFlowKey(ip, type) {
    return util.format("flow:conn:%s:%s", type, ip);
  }
  addFlow(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return rclient.zaddAsync(key, flow.ts, JSON.stringify(flow));
  }

  removeFlow(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return rclient.zremAsync(key, JSON.stringify(flow))
  }

  flowExists(ip, type, flow) {
    let key = this.getFlowKey(ip, type);

    if(typeof flow !== 'object') {
      return Promise.reject("Invalid flow type: " + typeof flow);
    }

    return async(() => {
      let result = await(rclient.zscoreAsync(key, JSON.stringify(flow)));

      if(result == null) {
        return false;
      } else {
        return true;
      }
    })();
  }

  queryFlows(ip, type, begin, end) {
    let key = this.getFlowKey(ip, type);

    return rclient.zrangebyscoreAsync(key, "(" + begin, end) // char '(' means open interval
      .then((flowStrings) => {
        return flowStrings.map((flowString) => JSON.parse(flowString));
      })
  }

  getDestIP(flow) {
    if(flow.lh === flow.sh) {
      return flow.dh;
    } else {
      return flow.sh;
    }
  }

  getDownloadTraffic(flow) {
    if(flow.lh === flow.sh) {
      return flow.rb;
    } else {
      return flow.ob;
    }
  }

  getUploadTraffic(flow) {
    if(flow.lh === flow.sh) {
      return flow.ob;
    } else {
      return flow.rb;
    }
  }
}

module.exports = function() {
  return new FlowTool();
};
<|endoftext|>


<commit_before>$(document).ready(function () {
    (function() {
        var count = 0;

        function print() {
            console.log(count);
        }

        function increase() {
            count++;
        }
    })();

    (function() {
        var module = new Object({
            count: 0,

            print: function() {
                console.log(this.count);
            },

            increase: function() {
                this.count++;
            }
        });

        module.count = 5;
        module.increase();
        module.print(); // output: 6
    })();

    (function() {
        var module = function() {
            var count = 0;

            var print = function() {
                console.log(count);
            };

            var increase = function() {
                count++;
            };

            return {
                print: print,
                increase: increase
            };
        }();

        module.count = 5;
        module.increase();
        module.print(); // output: 1
    })();
});

<commit_msg>Changes on comments<commit_after>$(document).ready(function () {
    (function() {
        var count = 0;

        function print() {
            console.log(count);
        }

        function increase() {
            count++;
        }
    })();

    (function() {
        var module = new Object({
            count: 0,

            print: function() {
                console.log(this.count);
            },

            increase: function() {
                this.count++;
            }
        });

        module.count = 5;
        module.increase();
        module.print(); // 6
    })();

    (function() {
        var module = function() {
            var count = 0;

            var print = function() {
                console.log(count);
            };

            var increase = function() {
                count++;
            };

            return {
                print: print,
                increase: increase
            };
        }();

        module.count = 5;
        module.increase();
        module.print(); // 1
    })();
});

<|endoftext|>


<commit_before>/* See license.txt for terms of usage */
var FireDiff  = FireDiff || {};

FireDiff.reps = FBL.ns(function() { with (FBL) {

const dateFormat = CCSV("@mozilla.org/intl/scriptabledateformat;1", "nsIScriptableDateFormat");

var Events = FireDiff.events,
    Path = FireDiff.Path,
    CSSModel = FireDiff.CSSModel,
    DiffDomplate = FireDiff.domplate;

var i18n = document.getElementById("strings_firediff");

// Object used to define the monitor view
this.Monitor = domplate({
  entry: DIV(
      {class: "diffMonitorElement", $firebugDiff: "$change|isFirebugDiff", $appDiff: "$change|isAppDiff", _repObject: "$change"},
      SPAN({class: "diffSummary"}, "$change|getSummary"),
      SPAN({class: "diffSep"}, ":"),
      SPAN({class: "diffSource"}, "$change|getDiffSource"),
      SPAN({class: "diffDate"}, "$change|getDate"),
      DIV({class: "diffXPath"}, "$change|getXPath"),
      DIV({class: "logEntry"}, TAG("$change|getChangeTag", {change: "$change", object: "$change.target"}))
      ),

  getChangeTag: function(change) {
    if (change.changeType == "CSS") {
      return DiffDomplate.CSSChanges.CSSStyleRule.tag;
    } else if (change.clone instanceof Text) {
      return DiffDomplate.TextChanged.tag;
    } else {
      return DiffDomplate.ElementChanged.tag;
    }
  },
  getSummary: function(change) {
    return change.getSummary();
  },
  getDiffSource: function(change) {
    if (this.isFirebugDiff(change)) {
      return i18n.getString("source.firebug");
    } else {
      return i18n.getString("source.application");
    }
  },
  getDate: function(change) {
    var date = change.date;
    return dateFormat.FormatDateTime(
        "", dateFormat.dateFormatLong, dateFormat.timeFormatSeconds,
        date.getFullYear(), date.getMonth() + 1, date.getDate(),
        date.getHours(), date.getMinutes(), date.getSeconds());
  },
  getXPath: function(change) {
    return change.displayXPath || change.xpath || "";
  },
  isFirebugDiff: function(change) {
    return change.changeSource == Events.ChangeSource.FIREBUG_CHANGE;
  },
  isAppDiff: function(change) {
    return change.changeSource == Events.ChangeSource.APP_CHANGE;
  },

  getChanges: function() {
    return Firebug.DiffModule.getChanges();
  },
  getTag: function(object) {
    return this.entry;
  },

  show: function(panel) {
    var changes = Firebug.DiffModule.getChanges();
    for (var i = 0; i < changes.length; i++) {
      this.onChange(changes[i], panel);
    }
  },
  onChange: function(change, panel) {
    try {
      this.entry.append({change: change}, panel.panelNode);
    } catch (err) {
      FBTrace.sysout("ERROR: onChange", err);
    }
  }
});
this.MonitorRep = domplate(Firebug.Rep,{
  supportsObject: function(object, type) {
    return object == FireDiff.reps.Monitor;
  },
  getTitle: function(object) {
    return i18n.getString("page.ChangeLog");
  }
});

function Snapshot(change) {
  var changes = Firebug.DiffModule.getChanges();
  var displayChanges = [], revertChanges = [];
  var foundChange = false;
  for (var i = 0; i < changes.length; i++) {
    if (changes[i] == change) {
      displayChanges.push(changes[i]);
      foundChange = true;
    } else if (change.sameFile(changes[i])) {
      (foundChange ? revertChanges : displayChanges).push(changes[i]);
    }
  }
  displayChanges = Events.merge(displayChanges);

  this.displayChanges = displayChanges;
  this.revertChanges = revertChanges;
}
Snapshot.prototype = {
  updateCloneToChange: function(clone, cloneXPath) {
    this.changeNodeList = [];
    if (FBTrace.DBG_FIREDIFF)   FBTrace.sysout("Revert changes", this.revertChanges);

    for (var i = this.revertChanges.length; i > 0; i--) {
      try {
        this.revertChanges[i-1].revert(clone, cloneXPath);
      } catch (err) {
        FBTrace.sysout("Snapshot.updateCloneToChane: revert " + i + " " + err, this.revertChanges[i-1]);
        throw err;
      }
    }
    for (var i = 0; i < this.displayChanges.length; i++) {
      this.changeNodeList.push(this.displayChanges[i].annotateTree(clone, cloneXPath));
    }
    this.normalizeChangeNodes();
  },

  navigableChange: function(changeNode) {},
  iterateChanges: function(stepper) {
    var change = stepper(this.curChange);

    for (var i = 0; i < this.changeNodeList.length+1; i++) {
      if (change >= this.changeNodeList.length) {
        change = 0;
      } else if (change < 0) {
        change = this.changeNodeList.length - 1;
      }

      if (this.navigableChange(this.changeNodeList[change])) {
        return change;
      }

      change = stepper(change);
    }
    return -1;
  },

  showNext: function() {
    this.curChange = this.iterateChanges(
        function(change) { return change + 1; });

    this.showCurNode();
  },
  showPrev: function() {
    this.curChange = this.iterateChanges(
        function(change) { return change - 1; });

    this.showCurNode();
  },
  getCurNode: function() {},
  showCurNode: function() {
    if (this.curChange < 0) {
      return;
    }

    var objectBox = this.getCurNode();
    if (objectBox) {
      scrollIntoCenterView(objectBox);
      setClassTimed(objectBox, "jumpHighlight", this.panel.context);
    }
    return objectBox;
  },
  getChangeNodePath: function(changeNode) {},
  normalizeChangeNodes: function() {
    // Reduce to one element per xpath
    var pathList = {};
    var ret = [];
    for (var i = 0; i < this.changeNodeList.length; i++) {
      var change = this.changeNodeList[i];
      var path = change.xpath || (this.cloneXPath + Path.getElementPath(change));

      if (!change.normalized) {
        change.lookupXPath = path;
        change.normalized = true;
        ret.push(change);
      }
    }

    ret.sort(function(a, b) { return Path.compareXPaths(a.lookupXPath, b.lookupXPath); });

    this.changeNodeList = ret;
  }
};

this.DOMSnapshot = function(change, document){
  Snapshot.call(this, change);

  this.displayTree = document.documentElement.cloneNode(true);
  this.cloneXPath = Path.getElementPath(document.documentElement);
  this.updateCloneToChange(this.displayTree, this.cloneXPath);

  this.onMouseDown = bind(this.onMouseDown, this);
};
this.DOMSnapshot.prototype = extend(Snapshot.prototype, {
  show: function(panel) {
    this.panel = panel;

    this.ioBox = new InsideOutBox(
        new DiffDomplate.HtmlSnapshotView(this.displayTree, this.cloneXPath, panel),
        panel.panelNode);
    this.ioBox.openObject(this.displayTree);

    if (FBTrace.DBG_FIREDIFF)   FBTrace.sysout("DOMSnapshot.changeNodeList", this.changeNodeList);
    for (var i = 0; i < this.changeNodeList.length; i++) {
      this.ioBox.openToObject(this.changeNodeList[i]);
    }
    this.curChange = -1;
    panel.panelNode.scrollTop = 0;

    panel.panelNode.addEventListener("mousedown", this.onMouseDown, false);
  },
  hide: function(panel) {
    if (this.ioBox) {
      this.ioBox.destroy();
      delete this.ioBox;
    }

    panel.panelNode.removeEventListener("mousedown", this.onMouseDown, false);

    delete this.panel;
  },

  navigableChange: function(changeNode) {
    var displayedTypes = {};
    displayedTypes[Events.ChangeSource.APP_CHANGE] = this.panel.isDisplayAppChanges();
    displayedTypes[Events.ChangeSource.FIREBUG_CHANGE] = this.panel.isDisplayFirebugChanges();

    // Accept the change if
    //  - Is not whitespace only or we are displaying whitespace
    //  - Is an app change and we are displaying app changes
    //  - Is a firebug change and we are displaying firebug changes
    if (!Firebug.showWhitespaceNodes && DiffDomplate.DomUtil.isWhitespaceText(changeNode)) {
      return false;
    }

    var change = changeNode[Events.AnnotateAttrs.CHANGES] || changeNode;
    if (displayedTypes[change.changeSource]) {
      return true;
    }
    var changes = changeNode[Events.AnnotateAttrs.ATTR_CHANGES] || {};
    for (var i in changes) {
      if (displayedTypes[changes[i].changeSource]) {
        return true;
      }
    }
  },
  getCurNode: function() {
    var change = this.changeNodeList[this.curChange]
    var objectBox = this.ioBox.openToObject(change);

    if (objectBox) {
      // For dom removed and events that register themselves as the elements
      // sole change, highlight the entire element, otherwise
      // highlight the label only (this should only be the attr case)
      if (change.subType == "dom_removed" || change[FireDiff.events.AnnotateAttrs.CHANGES]) {
        return objectBox;
      } else {
        return getChildByClass(objectBox.firstChild, 'nodeLabelBox') || objectBox;
      }
    }
  },

  onMouseDown: function(event) {
    if (isLeftClick(event) && getAncestorByClass(event.target, "nodeContainerLabel")) {
      this.ioBox.expandObject(Firebug.getRepObject(event.target));
    }
  }
});
this.DOMSnapshotRep = domplate(Firebug.Rep, {
  supportsObject: function(object, type) {
    return object instanceof FireDiff.reps.DOMSnapshot;
  },
  getTitle: function(object) {
    return i18n.getString("page.DOMSnapshot");
  }
});

this.CSSSnapshot = function(change, context){
  Snapshot.call(this, change);

  var rootPath = Path.getTopPath(change.xpath);
  this.sheet = Path.evaluateStylePath(rootPath, context.window.document);
  this.displayTree = CSSModel.cloneCSSObject(this.sheet);
  this.updateCloneToChange(this.displayTree, rootPath);
};
this.CSSSnapshot.prototype = extend(Snapshot.prototype, {
  show: function(panel) {
    this.panel = panel;
    DiffDomplate.CSSChanges.CSSList.tag.append({change: this.displayTree}, panel.panelNode);

    this.curChange = -1;
    panel.panelNode.scrollTop = 0;
  },
  hide: function() {
    delete this.panel;
  },

  navigableChange: function(change) {
    return this.panel.isDisplayFirebugChanges();
  },
  getCurNode: function() {
    var change = this.changeNodeList[this.curChange];
    return Firebug.getElementByRepObject(this.panel.panelNode, change);
  }
});
this.CSSSnapshotRep = domplate(Firebug.Rep, {
  supportsObject: function(object, type) {
    return object instanceof FireDiff.reps.CSSSnapshot;
  },
  getTitle: function(object) {
    return i18n.getString("page.CSSSnapshot");
  }
});

Firebug.registerRep(
    this.MonitorRep,
    this.DOMSnapshotRep,
    this.CSSSnapshotRep);
}});<commit_msg>Fix normalization logic for multiple snapshots.<commit_after>/* See license.txt for terms of usage */
var FireDiff  = FireDiff || {};

FireDiff.reps = FBL.ns(function() { with (FBL) {

const dateFormat = CCSV("@mozilla.org/intl/scriptabledateformat;1", "nsIScriptableDateFormat");

var Events = FireDiff.events,
    Path = FireDiff.Path,
    CSSModel = FireDiff.CSSModel,
    DiffDomplate = FireDiff.domplate;

var i18n = document.getElementById("strings_firediff");

// Object used to define the monitor view
this.Monitor = domplate({
  entry: DIV(
      {class: "diffMonitorElement", $firebugDiff: "$change|isFirebugDiff", $appDiff: "$change|isAppDiff", _repObject: "$change"},
      SPAN({class: "diffSummary"}, "$change|getSummary"),
      SPAN({class: "diffSep"}, ":"),
      SPAN({class: "diffSource"}, "$change|getDiffSource"),
      SPAN({class: "diffDate"}, "$change|getDate"),
      DIV({class: "diffXPath"}, "$change|getXPath"),
      DIV({class: "logEntry"}, TAG("$change|getChangeTag", {change: "$change", object: "$change.target"}))
      ),

  getChangeTag: function(change) {
    if (change.changeType == "CSS") {
      return DiffDomplate.CSSChanges.CSSStyleRule.tag;
    } else if (change.clone instanceof Text) {
      return DiffDomplate.TextChanged.tag;
    } else {
      return DiffDomplate.ElementChanged.tag;
    }
  },
  getSummary: function(change) {
    return change.getSummary();
  },
  getDiffSource: function(change) {
    if (this.isFirebugDiff(change)) {
      return i18n.getString("source.firebug");
    } else {
      return i18n.getString("source.application");
    }
  },
  getDate: function(change) {
    var date = change.date;
    return dateFormat.FormatDateTime(
        "", dateFormat.dateFormatLong, dateFormat.timeFormatSeconds,
        date.getFullYear(), date.getMonth() + 1, date.getDate(),
        date.getHours(), date.getMinutes(), date.getSeconds());
  },
  getXPath: function(change) {
    return change.displayXPath || change.xpath || "";
  },
  isFirebugDiff: function(change) {
    return change.changeSource == Events.ChangeSource.FIREBUG_CHANGE;
  },
  isAppDiff: function(change) {
    return change.changeSource == Events.ChangeSource.APP_CHANGE;
  },

  getChanges: function() {
    return Firebug.DiffModule.getChanges();
  },
  getTag: function(object) {
    return this.entry;
  },

  show: function(panel) {
    var changes = Firebug.DiffModule.getChanges();
    for (var i = 0; i < changes.length; i++) {
      this.onChange(changes[i], panel);
    }
  },
  onChange: function(change, panel) {
    try {
      this.entry.append({change: change}, panel.panelNode);
    } catch (err) {
      FBTrace.sysout("ERROR: onChange", err);
    }
  }
});
this.MonitorRep = domplate(Firebug.Rep,{
  supportsObject: function(object, type) {
    return object == FireDiff.reps.Monitor;
  },
  getTitle: function(object) {
    return i18n.getString("page.ChangeLog");
  }
});

function Snapshot(change) {
  var changes = Firebug.DiffModule.getChanges();
  var displayChanges = [], revertChanges = [];
  var foundChange = false;
  for (var i = 0; i < changes.length; i++) {
    if (changes[i] == change) {
      displayChanges.push(changes[i]);
      foundChange = true;
    } else if (change.sameFile(changes[i])) {
      (foundChange ? revertChanges : displayChanges).push(changes[i]);
    }
  }
  displayChanges = Events.merge(displayChanges);

  this.displayChanges = displayChanges;
  this.revertChanges = revertChanges;
}
Snapshot.prototype = {
  updateCloneToChange: function(clone, cloneXPath) {
    this.changeNodeList = [];
    if (FBTrace.DBG_FIREDIFF)   FBTrace.sysout("Revert changes", this.revertChanges);

    for (var i = this.revertChanges.length; i > 0; i--) {
      try {
        this.revertChanges[i-1].revert(clone, cloneXPath);
      } catch (err) {
        FBTrace.sysout("Snapshot.updateCloneToChane: revert " + i + " " + err, this.revertChanges[i-1]);
        throw err;
      }
    }
    for (var i = 0; i < this.displayChanges.length; i++) {
      this.changeNodeList.push(this.displayChanges[i].annotateTree(clone, cloneXPath));
    }
    this.normalizeChangeNodes();
  },

  navigableChange: function(changeNode) {},
  iterateChanges: function(stepper) {
    var change = stepper(this.curChange);

    for (var i = 0; i < this.changeNodeList.length+1; i++) {
      if (change >= this.changeNodeList.length) {
        change = 0;
      } else if (change < 0) {
        change = this.changeNodeList.length - 1;
      }

      if (this.navigableChange(this.changeNodeList[change])) {
        return change;
      }

      change = stepper(change);
    }
    return -1;
  },

  showNext: function() {
    this.curChange = this.iterateChanges(
        function(change) { return change + 1; });

    this.showCurNode();
  },
  showPrev: function() {
    this.curChange = this.iterateChanges(
        function(change) { return change - 1; });

    this.showCurNode();
  },
  getCurNode: function() {},
  showCurNode: function() {
    if (this.curChange < 0) {
      return;
    }

    var objectBox = this.getCurNode();
    if (objectBox) {
      scrollIntoCenterView(objectBox);
      setClassTimed(objectBox, "jumpHighlight", this.panel.context);
    }
    return objectBox;
  },
  getChangeNodePath: function(changeNode) {},
  normalizeChangeNodes: function() {
    // Reduce to one element per xpath
    var pathList = {};
    var ret = [];
    if (FBTrace.DBG_FIREDIFF)   FBTrace.sysout("Snapshot.normalizeChangeNodes prior", this.changeNodeList);

    for (var i = 0; i < this.changeNodeList.length; i++) {
      var change = this.changeNodeList[i];
      var path = change.xpath || (this.cloneXPath + Path.getElementPath(change));

      if (!change.normalized) {
        change.lookupXPath = path;
        change.normalized = true;
        ret.push(change);
      }
    }

    ret.sort(function(a, b) { return Path.compareXPaths(a.lookupXPath, b.lookupXPath); });

    // Since we are operating on a shared object we need to revert our tracking
    // var for future operations.
    for (var i = 0; i < this.changeNodeList.length; i++) {
      var change = this.changeNodeList[i];
      change.normalized = undefined;
    }

    this.changeNodeList = ret;
    if (FBTrace.DBG_FIREDIFF)   FBTrace.sysout("Snapshot.normalizeChangeNodes post", this.changeNodeList);
  }
};

this.DOMSnapshot = function(change, document){
  Snapshot.call(this, change);

  this.displayTree = document.documentElement.cloneNode(true);
  this.cloneXPath = Path.getElementPath(document.documentElement);
  this.updateCloneToChange(this.displayTree, this.cloneXPath);

  this.onMouseDown = bind(this.onMouseDown, this);
};
this.DOMSnapshot.prototype = extend(Snapshot.prototype, {
  show: function(panel) {
    this.panel = panel;

    this.ioBox = new InsideOutBox(
        new DiffDomplate.HtmlSnapshotView(this.displayTree, this.cloneXPath, panel),
        panel.panelNode);
    this.ioBox.openObject(this.displayTree);

    for (var i = 0; i < this.changeNodeList.length; i++) {
      this.ioBox.openToObject(this.changeNodeList[i]);
    }
    this.curChange = -1;
    panel.panelNode.scrollTop = 0;

    panel.panelNode.addEventListener("mousedown", this.onMouseDown, false);
  },
  hide: function(panel) {
    if (this.ioBox) {
      this.ioBox.destroy();
      delete this.ioBox;
    }

    panel.panelNode.removeEventListener("mousedown", this.onMouseDown, false);

    delete this.panel;
  },

  navigableChange: function(changeNode) {
    var displayedTypes = {};
    displayedTypes[Events.ChangeSource.APP_CHANGE] = this.panel.isDisplayAppChanges();
    displayedTypes[Events.ChangeSource.FIREBUG_CHANGE] = this.panel.isDisplayFirebugChanges();

    // Accept the change if
    //  - Is not whitespace only or we are displaying whitespace
    //  - Is an app change and we are displaying app changes
    //  - Is a firebug change and we are displaying firebug changes
    if (!Firebug.showWhitespaceNodes && DiffDomplate.DomUtil.isWhitespaceText(changeNode)) {
      return false;
    }

    var change = changeNode[Events.AnnotateAttrs.CHANGES] || changeNode;
    if (displayedTypes[change.changeSource]) {
      return true;
    }
    var changes = changeNode[Events.AnnotateAttrs.ATTR_CHANGES] || {};
    for (var i in changes) {
      if (displayedTypes[changes[i].changeSource]) {
        return true;
      }
    }
  },
  getCurNode: function() {
    var change = this.changeNodeList[this.curChange]
    var objectBox = this.ioBox.openToObject(change);

    if (objectBox) {
      // For dom removed and events that register themselves as the elements
      // sole change, highlight the entire element, otherwise
      // highlight the label only (this should only be the attr case)
      if (change.subType == "dom_removed" || change[FireDiff.events.AnnotateAttrs.CHANGES]) {
        return objectBox;
      } else {
        return getChildByClass(objectBox.firstChild, 'nodeLabelBox') || objectBox;
      }
    }
  },

  onMouseDown: function(event) {
    if (isLeftClick(event) && getAncestorByClass(event.target, "nodeContainerLabel")) {
      this.ioBox.expandObject(Firebug.getRepObject(event.target));
    }
  }
});
this.DOMSnapshotRep = domplate(Firebug.Rep, {
  supportsObject: function(object, type) {
    return object instanceof FireDiff.reps.DOMSnapshot;
  },
  getTitle: function(object) {
    return i18n.getString("page.DOMSnapshot");
  }
});

this.CSSSnapshot = function(change, context){
  Snapshot.call(this, change);

  var rootPath = Path.getTopPath(change.xpath);
  this.sheet = Path.evaluateStylePath(rootPath, context.window.document);
  this.displayTree = CSSModel.cloneCSSObject(this.sheet);
  this.updateCloneToChange(this.displayTree, rootPath);
};
this.CSSSnapshot.prototype = extend(Snapshot.prototype, {
  show: function(panel) {
    this.panel = panel;
    DiffDomplate.CSSChanges.CSSList.tag.append({change: this.displayTree}, panel.panelNode);

    this.curChange = -1;
    panel.panelNode.scrollTop = 0;
  },
  hide: function() {
    delete this.panel;
  },

  navigableChange: function(change) {
    return this.panel.isDisplayFirebugChanges();
  },
  getCurNode: function() {
    var change = this.changeNodeList[this.curChange];
    return Firebug.getElementByRepObject(this.panel.panelNode, change);
  }
});
this.CSSSnapshotRep = domplate(Firebug.Rep, {
  supportsObject: function(object, type) {
    return object instanceof FireDiff.reps.CSSSnapshot;
  },
  getTitle: function(object) {
    return i18n.getString("page.CSSSnapshot");
  }
});

Firebug.registerRep(
    this.MonitorRep,
    this.DOMSnapshotRep,
    this.CSSSnapshotRep);
}});<|endoftext|>


<commit_before>import { AbstractApplication } from 'yabf'
import program from 'commander'

import { LogUtils } from '../shared/utils'
import { MessageService } from '../services'

import { Command } from './command'

export class TermApplication extends AbstractApplication {
  get version() {
    return this._version || '0.0.0'
  }

  set version(semanticVersion) {
    this._version = semanticVersion
  }

  get description() {
    return this._desc
  }

  set description(text) {
    this._desc = text
  }

  constructor(injector, messageService) {
    super(injector)

    this.messageService = messageService
  }

  buildInstructions() {
    return [
      { provide: TermApplication, dependencies: [MessageService] },
      { provide: MessageService, dependencies: [] }
    ]
  }

  register(command, deps = []) {
    if (!(command.prototype instanceof Command)) {
      return null
    }

    this.commands = [command, ...(this.commands || [])]
    this.injectorService.provide(command, deps, false)
  }

  start() {
    if (!Array.isArray(this.commands) || this.commands.length < 1) {
      LogUtils.log({ message: `No command found in Term Application` })

      this.messageService.printError('Application failed to initialize')
      return
    }

    program
      .version(this.version)
      .description(this.description)

    for (const commandClass of this.commands) {
      const controller = this.injectorService.get(commandClass)

      let command = program.command(controller.commandName)

      if (controller.alias != null) {
        command = command.alias(controller.alias)
      }

      if (controller.alias != null && controller.description.length > 0) {
        command = command.description(controller.description)
      }

      if (Array.isArray(controller.options)) {
        for (const [option, optionDesc] of controller.options) {
          command = command.option(option, optionDesc)
        }
      }

      if ('run' in controller && typeof controller.run === 'function') {
        const action = controller.run.bind(controller)
        const wrappedAction = this._wrapActions(action)

        command.action(wrappedAction)
      }
    }

    program.parse(process.argv)
  }

  _wrapActions(action) {
    return (...args) => {
      try {
        action(...args)
      } catch (error) {
        this.messageService.printError('Fail', 'An error occured.')
      }
    }
  }
}
<commit_msg>Use MessageService in TermApplication<commit_after>import { AbstractApplication } from 'yabf'
import program from 'commander'

import { MessageService } from '../services'

import { Command } from './command'

export class TermApplication extends AbstractApplication {
  get version() {
    return this._version || '0.0.0'
  }

  set version(semanticVersion) {
    this._version = semanticVersion
  }

  get description() {
    return this._desc
  }

  set description(text) {
    this._desc = text
  }

  constructor(injector, messageService) {
    super(injector)

    this.messageService = messageService
  }

  buildInstructions() {
    return [
      { provide: TermApplication, dependencies: [MessageService] },
      { provide: MessageService, dependencies: [] }
    ]
  }

  register(command, deps = []) {
    if (!(command.prototype instanceof Command)) {
      return null
    }

    this.commands = [command, ...(this.commands || [])]
    this.injectorService.provide(command, deps, false)
  }

  start() {
    if (!Array.isArray(this.commands) || this.commands.length < 1) {
      this.messageService.print(`No command found in Term Application`)
      this.messageService.printError('Application failed to initialize')
      return
    }

    program
      .version(this.version)
      .description(this.description)

    for (const commandClass of this.commands) {
      const controller = this.injectorService.get(commandClass)

      let command = program.command(controller.commandName)

      if (controller.alias != null) {
        command = command.alias(controller.alias)
      }

      if (controller.alias != null && controller.description.length > 0) {
        command = command.description(controller.description)
      }

      if (Array.isArray(controller.options)) {
        for (const [option, optionDesc] of controller.options) {
          command = command.option(option, optionDesc)
        }
      }

      if ('run' in controller && typeof controller.run === 'function') {
        const action = controller.run.bind(controller)
        const wrappedAction = this._wrapActions(action)

        command.action(wrappedAction)
      }
    }

    program.parse(process.argv)
  }

  _wrapActions(action) {
    return (...args) => {
      try {
        action(...args)
      } catch (error) {
        this.messageService.printError('Fail', 'An error occured.')
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * old school news ticker
 * @module dlTicker
 * @author (c) 2014 Eric Herrmann, MIT License
 */

/**
 * Description for ticker class.
 *
 * @class DLTicker
 * @static
 */
var DLTicker = function() {

    /*
     * @property allTickers
     * @type {Array}
     */
    var allTickers = null;

    /*
     * @property currTicker
     * @type {Integer}
     */
    var currTicker = 0;

    /*
     * @property allNodes
     * @type {Array}
     */
    var allNodes = null;

    /*
     * @property currNode
     * @type {Integer}
     */
    var currNode = 0;

    /*
     * @property charPos
     * @type {Integer}
     */
    var charPos = 0;

    /*
     * @property dlTimer
     * @type {Object}
     */
    var dlTimer = null;

    /*
     * @property
     * @type {Integer}
     * @default 10
     */
    var speed = 10;

    /*
     * @property
     * @type {function}
     */
    var finishCallback;

    /*
     * @property
     * @type {object} saves the reference to the object created with new
     */
    var that = this;

    /*
     * @method getElementsByClassName
     * @param {String} class name
     * @return {Array} of dom elements which has been found
     */
    var getElementsByClassName = function(className) {
        var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"),
            all     = document.getElementsByTagName('div'),
            found   = [],
            i;

        var divCount = all.length;

        for (i = 0; i < divCount; i++) {
            if (all[i] && all[i].className && all[i].className !== '') {
                if (all[i].className.match(pattern) && all[i].id !== '') {
                    found[found.length] = all[i];
                }
            }
        }
        return found;
    };

    /*
     *
     * @method start
     * @param {Integer} speed from 1..~100 in miliseconds
     * @param {function} optional callback
     */
    this.start = function(setSpeed, callback) {
        if (typeof setSpeed !== 'undefined') {
            speed = setSpeed;
        }
        if (typeof callback === 'function') {
            finishCallback = callback;
        }

        allTickers = getElementsByClassName('ticker');

        if (allTickers.length === 0) {
            return;
        }

        init(allTickers[currTicker].id);
    };

    /*
     * @method init
     * @param {String} id of current div
     */
    var init = function(divId) {
        var currDiv = document.getElementById(divId);
        allNodes    = currDiv.cloneNode(true).childNodes;

        // start ticker sound if available
        if (typeof that.soundstart === 'function') {
            that.soundstart();
        }

        // identify animation
        if (currDiv.getAttribute('data-dl-anim') !== null) {
            drawAnim(currDiv.getAttribute('data-dl-anim'), currDiv);
            return;
        }

        // ensure that there are nodes in this div
        if (allNodes.length === 0) {
            initNextTicker();
            return;
        }

        // draw nodes
        currNode              = 0;
        currDiv.innerHTML     = '';
        currDiv.style.display = 'block';
        printNextNode(currDiv, currNode);
    };

    /*
     * @method drawAnim
     * @param {String} sprite data e.g. "270-118-40" "width-imagecount-interval"
     * @param {Object}
     */
    var drawAnim = function(spriteData, currDiv) {
        var thatDiv     = currDiv;
        var data        = spriteData.split('-');
        var picWidth    = data[0];
        var left        = 0;
        var interations = data[1];
        var interval    = data[2];
        var maxValue    = interations * picWidth * -1;
        var spriteTimer;
        initNextTicker();

        spriteTimer = setInterval(function() {
          thatDiv.style.backgroundPosition = ( left -= picWidth ) + "px 0px";
          if ( left <= maxValue ) {
            clearInterval( spriteTimer );
          }
        }, interval);

    };

    /*
     * @method initPrintChar
     * @param {Object} current DOM div
     * @param {String}
     * @param {Object} DOM node
     * @param {String} content attribute
     */
    var initPrintChar = function(currDiv, nodeText, newNode, content_attribute) {
        nodeText        = nodeText.replace(/(\r\n)|(\r)|(\n)/g, '');
        var nodeTextLen = nodeText.length;
        charPos         = 0;
        dlTimer         = setInterval(function() {
            printChar(currDiv, nodeText, nodeTextLen, newNode, content_attribute);
        }, speed);
    };

    /*
     * @method printNextNode
     * @param {Object} current DOM div
     */
    var printNextNode = function(currDiv) {
        if (allNodes[currNode].nodeName === '#text') {
            var nodeText = allNodes[currNode].nodeValue;
            var newNode  = document.createTextNode('');
            currDiv.appendChild(newNode);
            initPrintChar(currDiv, nodeText, newNode, 'nodeValue');
        } else if (allNodes[currNode].nodeName === 'A') {
            var nodeText = allNodes[currNode].text;
            allNodes[currNode].text = '';
            var newNode  = currDiv.appendChild(allNodes[currNode].cloneNode(true));
            initPrintChar(currDiv, nodeText, newNode, 'text');
        } else {
            currDiv.appendChild(allNodes[currNode].cloneNode(true));
            initNextNode(currDiv);
        }
    };

    /*
     * @method initNextNode
     * @param {Object} current DOM div
     */
    var initNextNode = function(currDiv) {
        if (currNode+1 < allNodes.length) {
            currNode++;
            printNextNode(currDiv);
        } else {
            currNode = 0;
            initNextTicker();
        }
    };

    /*
     * @method printChar
     * @param {Object} current DOM div
     * @param {String}
     * @param {Integer}
     * @param {Object} DOM node
     * @param {String} content attribute
     */
    var printChar = function(currDiv, nodeText, nodeTextLen, newNode, content_attribute) {
        if (charPos > (nodeTextLen - 1)) {
            clearInterval(dlTimer);
            initNextNode(currDiv);
            return;
        }
        newNode[content_attribute] = newNode[content_attribute] + nodeText.substring(charPos, charPos + 1);
        charPos++;
    };

    /*
     * @method initNextTicker
     */
    var initNextTicker = function() {
        if(currTicker+1 < allTickers.length) {
            currTicker++;
            init(allTickers[currTicker].id);
        } else {
            if (typeof that.soundstop === 'function') {
                that.soundstop();
            }
            if (typeof finishCallback === 'function') {
                finishCallback();
            }
        }
    };

  };
<commit_msg>fixed some code which was killed by accident<commit_after>/**
 * old school news ticker
 * @module dlTicker
 * @author (c) 2014 Eric Herrmann, MIT License
 */

/**
 * Description for ticker class.
 *
 * @class DLTicker
 * @static
 */
var DLTicker = function() {

    /*
     * @property allTickers
     * @type {Array}
     */
    var allTickers = null;

    /*
     * @property currTicker
     * @type {Integer}
     */
    var currTicker = 0;

    /*
     * @property allNodes
     * @type {Array}
     */
    var allNodes = null;

    /*
     * @property currNode
     * @type {Integer}
     */
    var currNode = 0;

    /*
     * @property charPos
     * @type {Integer}
     */
    var charPos = 0;

    /*
     * @property dlTimer
     * @type {Object}
     */
    var dlTimer = null;

    /*
     * @property
     * @type {Integer}
     * @default 10
     */
    var speed = 10;

    /*
     * @property
     * @type {function}
     */
    var finishCallback;

    /*
     * @property
     * @type {object} saves the reference to the object created with new
     */
    var that = this;

     /*
      * @property
      * @type {array} ids of ticker classes which should be tickered
      */
    var tickerIds = [];

    /*
     * @method getElementsByClassName
     * @param {String} class name
     * @return {Array} of dom elements which has been found
     */
    var getElementsByClassName = function(className) {
        var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"),
            all     = document.getElementsByTagName('div'),
            found   = [],
            i,
            j;

        var divCount = all.length;
        var tiIdsLen = tickerIds.length;

        for (i = 0; i < divCount; i++) {
            if (all[i] && all[i].className && all[i].className !== '') {
                if (all[i].className.match(pattern) && all[i].id !== '') {
                    if (tiIdsLen === 0) {
                        found[found.length] = all[i];
                    } else {
                        for (j = 0; j < tiIdsLen; j++) {
                            if (tickerIds[j] === all[i].id) {
                                found[found.length] = all[i];
                            }
                        }
                    }
                }
            }
        }
        return found;
    };

    /*
     *
     * @method start
     * @param {Integer} speed from 1..~100 in miliseconds
     * @param {function} optional callback routine
     * @param {Array} optional ticker ids
     */
    this.start = function(setSpeed, callback, setTickerIds) {
        if (typeof setSpeed !== 'undefined') {
            speed = setSpeed;
        }
        if (typeof callback === 'function') {
            finishCallback = callback;
        }

        if (setTickerIds !== null && typeof setTickerIds === 'object' && setTickerIds.length !== null) {
            tickerIds = setTickerIds;
        }

        allTickers = getElementsByClassName('ticker');

        if (allTickers.length === 0) {
            return;
        }

        init(allTickers[currTicker].id);
    };

    /*
     * @method init
     * @param {String} id of current div
     */
    var init = function(divId) {
        var currDiv = document.getElementById(divId);
        allNodes    = currDiv.cloneNode(true).childNodes;

        // start ticker sound if available
        if (typeof that.soundstart === 'function') {
            that.soundstart();
        }

        // identify animation
        if (currDiv.getAttribute('data-dl-anim') !== null) {
            drawAnim(currDiv.getAttribute('data-dl-anim'), currDiv);
            return;
        }

        // ensure that there are nodes in this div
        if (allNodes.length === 0) {
            initNextTicker();
            return;
        }

        // draw nodes
        currNode                 = 0;
        currDiv.innerHTML        = '';
        currDiv.style.visibility = 'visible';
        printNextNode(currDiv, currNode);
    };

    /*
     * @method drawAnim
     * @param {String} sprite data e.g. "270-119-40" "width-imagecount-interval"
     * @param {Object}
     */
    var drawAnim = function(spriteData, currDiv) {
        var thatDiv     = currDiv;
        var data        = spriteData.split('-');
        var picWidth    = data[0];
        var left        = 0;
        var interations = data[1] - 1;
        var interval    = data[2];
        var maxValue    = interations * picWidth * -1;
        var spriteTimer;
        initNextTicker();

        spriteTimer = setInterval(function() {
          thatDiv.style.backgroundPosition = ( left -= picWidth ) + "px 0px";
          if ( left <= maxValue ) {
            clearInterval( spriteTimer );
          }
        }, interval);

    };

    /*
     * @method initPrintChar
     * @param {Object} current DOM div
     * @param {String}
     * @param {Object} DOM node
     * @param {String} content attribute
     */
    var initPrintChar = function(currDiv, nodeText, newNode, content_attribute) {
        nodeText        = nodeText.replace(/(\r\n)|(\r)|(\n)/g, '');
        var nodeTextLen = nodeText.length;
        charPos         = 0;
        dlTimer         = setInterval(function() {
            printChar(currDiv, nodeText, nodeTextLen, newNode, content_attribute);
        }, speed);
    };

    /*
     * @method printNextNode
     * @param {Object} current DOM div
     */
    var printNextNode = function(currDiv) {
        if (allNodes[currNode].nodeName === '#text') {
            var nodeText = allNodes[currNode].nodeValue;
            var newNode  = document.createTextNode('');
            currDiv.appendChild(newNode);
            initPrintChar(currDiv, nodeText, newNode, 'nodeValue');
        } else if (allNodes[currNode].nodeName === 'A') {
            var nodeText = allNodes[currNode].text;
            allNodes[currNode].text = '';
            var newNode  = currDiv.appendChild(allNodes[currNode].cloneNode(true));
            initPrintChar(currDiv, nodeText, newNode, 'text');
        } else {
            currDiv.appendChild(allNodes[currNode].cloneNode(true));
            initNextNode(currDiv);
        }
    };

    /*
     * @method initNextNode
     * @param {Object} current DOM div
     */
    var initNextNode = function(currDiv) {
        if (currNode+1 < allNodes.length) {
            currNode++;
            printNextNode(currDiv);
        } else {
            currNode = 0;
            initNextTicker();
        }
    };

    /*
     * @method printChar
     * @param {Object} current DOM div
     * @param {String}
     * @param {Integer}
     * @param {Object} DOM node
     * @param {String} content attribute
     */
    var printChar = function(currDiv, nodeText, nodeTextLen, newNode, content_attribute) {
        if (charPos > (nodeTextLen - 1)) {
            clearInterval(dlTimer);
            initNextNode(currDiv);
            return;
        }
        newNode[content_attribute] = newNode[content_attribute] + nodeText.substring(charPos, charPos + 1);
        charPos++;
    };

    /*
     * @method initNextTicker
     */
    var initNextTicker = function() {
        if(currTicker+1 < allTickers.length) {
            currTicker++;
            init(allTickers[currTicker].id);
        } else {
            if (typeof that.soundstop === 'function') {
                that.soundstop();
            }
            if (typeof finishCallback === 'function') {
                finishCallback();
            }
        }
    };

  };
<|endoftext|>


<commit_before>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global define: false, console: false, window: false, setTimeout: false */

define( function( require ) {

    var Thread = function __thread( id ) {

        var console = {

                log: function() {
                    var message = Array.prototype.slice.call( arguments ).join( '\n' );
                    send( '__log', {
                        log: message
                    });
                }

        }

        // TD: propagate exceptions back to the main thread
        var assert = function( condition, message ) {
        };

        // Exposed functions are searched (by name) when looking for message handlers.
        var _exposed = {};
        var expose = function( f, alias ) {
            assert( f.name || alias );
            var name = f.name || alias;
            _exposed[ name ] = f;
        };

        var send = function( method, request ) {
            request = request || {};
            self.postMessage({
                method: method,
                thread: id,
                request: request
            });
        };

        self.onmessage = function( event ) {
            var message = event.data;
            if( _exposed[ message.method ] ) {
                _exposed[ message.method ]( message.request );
            } else {
                console.log( 'ignoring unknown method ' + message.method + ' from thread' );
            }
        };

        // TD: implement some error handling
        self.onerror = function( error ) {
        };

        var handle_dispatch = function __dispatch( message ) {
            // TD: Try/catch here to handle errors
            // Create a new function from the serialized data, wrap it to provide some additional parameters, then call it
            var f = new Function( ['console', 'assert', 'parameters'], 'var f = ' + message.callable + '; return f.apply( null, parameters );' );
            var result = f.apply( null, [ console, assert, message.parameters ] );
            send( '__result', {
                result: result
            });
            send( '__ready' );
        };
        expose( handle_dispatch );

        var handle_run = function __run( message ) {
            _id = message.id;
        };
        expose( handle_run );

        send( '__ready' );

    };

    return Thread;

});<commit_msg>Fixed worker console output.<commit_after>/*jshint white: false, strict: false, plusplus: false, onevar: false,
  nomen: false */
/*global define: false, console: false, window: false, setTimeout: false */

define( function( require ) {

    var Thread = function __thread( id ) {

        var console = {

                log: function() {
                    var message = Array.prototype.slice.call( arguments ).join( ' ' );
                    send( '__log', {
                        log: message
                    });
                }

        }

        // TD: propagate exceptions back to the main thread
        var assert = function( condition, message ) {
        };

        // Exposed functions are searched (by name) when looking for message handlers.
        var _exposed = {};
        var expose = function( f, alias ) {
            assert( f.name || alias );
            var name = f.name || alias;
            _exposed[ name ] = f;
        };

        var send = function( method, request ) {
            request = request || {};
            self.postMessage({
                method: method,
                thread: id,
                request: request
            });
        };

        self.onmessage = function( event ) {
            var message = event.data;
            if( _exposed[ message.method ] ) {
                _exposed[ message.method ]( message.request );
            } else {
                console.log( 'ignoring unknown method ' + message.method + ' from thread' );
            }
        };

        // TD: implement some error handling
        self.onerror = function( error ) {
        };

        var handle_dispatch = function __dispatch( message ) {
            // TD: Try/catch here to handle errors
            // Create a new function from the serialized data, wrap it to provide some additional parameters, then call it
            var f = new Function( ['console', 'assert', 'parameters'], 'var f = ' + message.callable + '; return f.apply( null, parameters );' );
            var result = f.apply( null, [ console, assert, message.parameters ] );
            send( '__result', {
                result: result
            });
            send( '__ready' );
        };
        expose( handle_dispatch );

        var handle_run = function __run( message ) {
            _id = message.id;
        };
        expose( handle_run );

        send( '__ready' );

    };

    return Thread;

});
<|endoftext|>


<commit_before>import config from 'config';
import fs from 'fs';
import path from 'path';
import md5File from 'md5-file';
import xml2js from 'xml2js';

export function uniqueID() {
  function chr4() {
    return Math.random().toString(16).slice(-4);
  }

  return chr4() + chr4() +
    '-' + chr4() +
    '-' + chr4() +
    '-' + chr4() +
    '-' + chr4() + chr4() + chr4();
}

// Traitement d'une ligne du fichier readme.txt des BIOS
const md5Rule = /^[a-f0-9]{32}$/i;
const biosPath = config.get('recalbox.biosPath');

export function handleBiosLine(line) {
  let parts = line.split(' ');

  if (!parts[0].match(md5Rule)) {
    return null;
  }

  parts = parts.filter(Boolean);
  const md5 = parts.shift();
  const name = parts.join(' ').trim();
  const thisBiosPath = path.join(biosPath, name);

  return {
    md5: md5,
    name: name,
    valid: fs.existsSync(thisBiosPath) ? md5 === md5File.sync(thisBiosPath) : null
  };
}

// Traitement des ROMs
export async function getRoms(system, subpath = '') {
  const srcpath = path.join(config.get('recalbox.romsPath'), system, subpath);
  const esSystems = await getEsSystems();
  const systemData = esSystems.find((s) => s.name === system);
  const romExtensions = systemData ? systemData.extensions : [];

  return fs.readdirSync(srcpath).filter((file) => {
    return fs.statSync(path.join(srcpath, file)).isFile() &&
      -1 !== romExtensions.indexOf(path.extname(file));
  });
}

// Promise xml2json
async function xmlToJson(file) {
  var promise = await new Promise((resolve, reject) => {
    const parser = new xml2js.Parser({
      trim: true,
      explicitArray: false
    });

    parser.parseString(fs.readFileSync(file), (jsError, jsResult) => {
      if (jsError) {
        reject(jsError);
      } else {
        resolve(jsResult);
      }
    });
  });

  return promise;
}

// Traitements des systèmes supportés par ES
let esSystems;

export async function getEsSystems() {
  if (esSystems) {
    return esSystems;
  }

  const json = await xmlToJson(config.get('recalbox.esSystemsCfgPath'));
  esSystems = [];

  json.systemList.system.forEach((system) => {
    esSystems.push({
      name: system.name,
      fullname: system.fullname,
      path: system.path,
      extensions: system.extension ? system.extension.split(' ') : [],
      launchCommand: system.command,
    });
  });

  return esSystems;
}

// Traitement des fichiers gamelist.xml
export function parseGameReleaseDate(releaseDate) {
  return {
    day: releaseDate.substring(6, 8),
    month: releaseDate.substring(4, 6),
    year: releaseDate.substring(0, 4),
  };
}

export function getSystemRomsBasePath(system) {
  return path.join(config.get('recalbox.romsPath'), system);
}

export function getSystemGamelistPath(system) {
  return path.join(getSystemRomsBasePath(system), 'gamelist.xml');
}

export async function getSystemGamelist(system, raw = false) {
  const json = await xmlToJson(getSystemGamelistPath(system));

  if (raw) {
    return json;
  }

  let list = {};
  let gameList = json.gameList.game || [];

  if (!Array.isArray(gameList)) {
    gameList = [gameList];
  }

  gameList.forEach((game) => {
    list[game.path.substring(2)] = game;
  });

  return list;
}
<commit_msg>[Server] Check existence of gamelist.xml<commit_after>import config from 'config';
import fs from 'fs';
import path from 'path';
import md5File from 'md5-file';
import xml2js from 'xml2js';

export function uniqueID() {
  function chr4() {
    return Math.random().toString(16).slice(-4);
  }

  return chr4() + chr4() +
    '-' + chr4() +
    '-' + chr4() +
    '-' + chr4() +
    '-' + chr4() + chr4() + chr4();
}

// Traitement d'une ligne du fichier readme.txt des BIOS
const md5Rule = /^[a-f0-9]{32}$/i;
const biosPath = config.get('recalbox.biosPath');

export function handleBiosLine(line) {
  let parts = line.split(' ');

  if (!parts[0].match(md5Rule)) {
    return null;
  }

  parts = parts.filter(Boolean);
  const md5 = parts.shift();
  const name = parts.join(' ').trim();
  const thisBiosPath = path.join(biosPath, name);

  return {
    md5: md5,
    name: name,
    valid: fs.existsSync(thisBiosPath) ? md5 === md5File.sync(thisBiosPath) : null
  };
}

// Traitement des ROMs
export async function getRoms(system, subpath = '') {
  const srcpath = path.join(config.get('recalbox.romsPath'), system, subpath);
  const esSystems = await getEsSystems();
  const systemData = esSystems.find((s) => s.name === system);
  const romExtensions = systemData ? systemData.extensions : [];

  return fs.readdirSync(srcpath).filter((file) => {
    return fs.statSync(path.join(srcpath, file)).isFile() &&
      -1 !== romExtensions.indexOf(path.extname(file));
  });
}

// Promise xml2json
async function xmlToJson(file) {
  var promise = await new Promise((resolve, reject) => {
    const parser = new xml2js.Parser({
      trim: true,
      explicitArray: false
    });

    parser.parseString(fs.readFileSync(file), (jsError, jsResult) => {
      if (jsError) {
        reject(jsError);
      } else {
        resolve(jsResult);
      }
    });
  });

  return promise;
}

// Traitements des systèmes supportés par ES
let esSystems;

export async function getEsSystems() {
  if (esSystems) {
    return esSystems;
  }

  const json = await xmlToJson(config.get('recalbox.esSystemsCfgPath'));
  esSystems = [];

  json.systemList.system.forEach((system) => {
    esSystems.push({
      name: system.name,
      fullname: system.fullname,
      path: system.path,
      extensions: system.extension ? system.extension.split(' ') : [],
      launchCommand: system.command,
    });
  });

  return esSystems;
}

// Traitement des fichiers gamelist.xml
export function parseGameReleaseDate(releaseDate) {
  return {
    day: releaseDate.substring(6, 8),
    month: releaseDate.substring(4, 6),
    year: releaseDate.substring(0, 4),
  };
}

export function getSystemRomsBasePath(system) {
  return path.join(config.get('recalbox.romsPath'), system);
}

export function getSystemGamelistPath(system) {
  return path.join(getSystemRomsBasePath(system), 'gamelist.xml');
}

export async function getSystemGamelist(system, raw = false) {
  const gameListPath = getSystemGamelistPath(system);

  if (!fs.existsSync(gameListPath)) {
    return {};
  }

  const json = await xmlToJson(gameListPath);

  if (raw) {
    return json;
  }

  let list = {};
  let gameList = json.gameList.game || [];

  if (!Array.isArray(gameList)) {
    gameList = [gameList];
  }

  gameList.forEach((game) => {
    list[game.path.substring(2)] = game;
  });

  return list;
}
<|endoftext|>


<commit_before>define(
  [
    'jquery',
    'controllers/NotificationController',
    'globals'
  ],
  function ($, NotificationController, globals) {

    return {

      sendFeedback: function (feedback) {

        var data = {};

        // The message from the use
        data["message"] = feedback;

        // Size information for the user's browser and monitor
        data['resolution'] = {
          'viewport': {
            'width': $(window).width(),
            'height': $(window).height()
          },
          'screen': {
            'width': screen.width,
            'height': screen.height
          }
        };

        var feedbackUrl = globals.API_ROOT + '/email/feedback' + globals.slash();

        $.ajax(feedbackUrl, {
          type: 'POST',
          data: JSON.stringify(data),
          success: function (data) {
            NotificationController.info("Thanks for your feedback!", "Support has been notified.");
          },
          error: function (response_text) {
            var errorMessage = "Your feedback could not be submitted. If you'd like to send it directly to support, email support@iplantcollaborative.org.";
            NotificationController.error("An error occured", errorMessage);
          }
        });

      }

    };

  });
<commit_msg>remove JSON.stringify to properly serialize data<commit_after>define(
  [
    'jquery',
    'controllers/NotificationController',
    'globals'
  ],
  function ($, NotificationController, globals) {

    return {

      sendFeedback: function (feedback) {

        var data = {};

        // The message from the use
        data["message"] = feedback;

        // Size information for the user's browser and monitor
        data['resolution'] = {
          'viewport': {
            'width': $(window).width(),
            'height': $(window).height()
          },
          'screen': {
            'width': screen.width,
            'height': screen.height
          }
        };

        var feedbackUrl = globals.API_ROOT + '/email/feedback' + globals.slash();

        $.ajax(feedbackUrl, {
          type: 'POST',
          data: data,
          success: function (data) {
            NotificationController.info("Thanks for your feedback!", "Support has been notified.");
          },
          error: function (response_text) {
            var errorMessage = "Your feedback could not be submitted. If you'd like to send it directly to support, email support@iplantcollaborative.org.";
            NotificationController.error("An error occured", errorMessage);
          }
        });

      }

    };

  });
<|endoftext|>


<commit_before>var komodoWakatime = {

    VERSION: '2.0.1',

    action_frequency: 2,
    time: 0,
    api_key: '',
    view: null,
    fileName: '',
    keyPressListener: null,

    onLoad: function (thisObject) {
        var prefs = Components.classes['@activestate.com/koPrefService;1']
          .getService(Components.interfaces.koIPrefService)
          .prefs;
        var pref_name = 'wakatime_api_key';
        komodoWakatime.initViewListener(komodoWakatime);
        if (!prefs.hasStringPref(pref_name) || prefs.getStringPref(pref_name) === '') {
            thisObject.api_key = thisObject.promptForAPIKey();
            prefs.setStringPref('wakatime_api_key', thisObject.api_key);
        } else {
            thisObject.api_key = prefs.getStringPref(pref_name);
        }
    },
    log: function(msg) {
        ko.logging.getLogger('').warn(msg);
    },
    apiClientLocation: function () {
        var currProfPath = Components.classes["@mozilla.org/file/directory_service;1"]
          .getService(Components.interfaces.nsIProperties)
          .get("PrefD", Components.interfaces.nsILocalFile)
          .path;
        var plugin_dir = currProfPath + '/extensions/wakatime@wakatime.com';
        return plugin_dir + '/components/wakatime/cli.py';
    },
    promptForAPIKey: function () {
        return ko.dialogs.prompt("[WakaTime] Enter your wakatime.com api key:");
    },
    getFileName: function (thisObject) {
        return thisObject.fileName;
    },
    enoughTimePassed: function (thisObject) {
        var d = new Date();
        if ((d.getTime() - thisObject.time) > thisObject.action_frequency * 60000) {
            thisObject.time = d.getTime();
            return true;
        }
        return false;
    },
    sendDataToAPI: function (thisObject, writeFlag) {
        writeFlag = typeof writeFlag !== 'undefined' ? writeFlag : false;
        var cmdWriteFlag = writeFlag ? '--write' : '';
        var fileName = thisObject.getFileName(thisObject);
        var cmd = 'python ' +
          thisObject.apiClientLocation().replace(/(@)/g, "\\@").replace(/(\s)/g, "\\ ") +
          ' ' + cmdWriteFlag + ' --file ' + fileName + ' --plugin komodo-wakatime/'+ thisObject.VERSION +
          ' --key ' + thisObject.api_key;
        var runSvc = Components.classes["@activestate.com/koRunService;1"]
          .createInstance(Components.interfaces.koIRunService);
        var process = runSvc.RunAndNotify(cmd, '', '', '');
    },
    keyPressEvent: function (thisObject) {
        if (thisObject.enoughTimePassed(thisObject)) {
            thisObject.sendDataToAPI(thisObject);
        }
    },
    fileSaveEvent: function (thisObject) {
        thisObject.sendDataToAPI(thisObject, true);
    },
    initViewListener: function (thisObject) {
        if (thisObject.view !== null) {
            thisObject.view.removeEventListener('keypress', thisObject.keyPressListener, true);
        }
        thisObject.keyPressListener = function (event) {
            thisObject.keyPressEvent(thisObject, event);
        };
        if (ko.views.manager.currentView !== null) {
            thisObject.view = ko.views.manager.currentView;
            thisObject.view.addEventListener('keypress', thisObject.keyPressListener, true);
        }
    },
    fileChanged: function (thisObject, event) {
        thisObject.fileName = event.originalTarget.koDoc.file.displayPath;
        ko.views.manager.currentView.removeEventListener('keypress', thisObject.keyPressListener, true);
        thisObject.keyPressListener = function (event) {
            thisObject.keyPressEvent(thisObject, event);
        };
        thisObject.view = event.originalTarget;
        thisObject.view.addEventListener('keypress', thisObject.keyPressListener, true);
    }
};

window.addEventListener('file_saved', function (event) {
    komodoWakatime.fileSaveEvent(komodoWakatime, event);
}, false);
window.addEventListener('current_view_changed', function (event) {
    komodoWakatime.fileChanged(komodoWakatime, event);
}, true);
window.addEventListener('komodo-ui-started', function (event) {
    komodoWakatime.onLoad(komodoWakatime, event);
}, true);
<commit_msg>create keyPressListener callback before using it<commit_after>var komodoWakatime = {

    VERSION: '2.0.1',

    action_frequency: 2,
    time: 0,
    api_key: '',
    view: null,
    fileName: '',
    keyPressListener: null,

    onLoad: function (thisObject) {
        var prefs = Components.classes['@activestate.com/koPrefService;1']
          .getService(Components.interfaces.koIPrefService)
          .prefs;
        var pref_name = 'wakatime_api_key';
        if (!prefs.hasStringPref(pref_name) || prefs.getStringPref(pref_name) === '') {
            thisObject.api_key = thisObject.promptForAPIKey();
            prefs.setStringPref('wakatime_api_key', thisObject.api_key);
        } else {
            thisObject.api_key = prefs.getStringPref(pref_name);
        }
        komodoWakatime.initViewListener(komodoWakatime);
    },
    log: function(msg) {
        ko.logging.getLogger('').warn(msg);
    },
    apiClientLocation: function () {
        var currProfPath = Components.classes["@mozilla.org/file/directory_service;1"]
          .getService(Components.interfaces.nsIProperties)
          .get("PrefD", Components.interfaces.nsILocalFile)
          .path;
        var plugin_dir = currProfPath + '/extensions/wakatime@wakatime.com';
        return plugin_dir + '/components/wakatime/cli.py';
    },
    promptForAPIKey: function () {
        return ko.dialogs.prompt("[WakaTime] Enter your wakatime.com api key:");
    },
    getFileName: function (thisObject) {
        return thisObject.fileName;
    },
    enoughTimePassed: function (thisObject) {
        var d = new Date();
        if ((d.getTime() - thisObject.time) > thisObject.action_frequency * 60000) {
            thisObject.time = d.getTime();
            return true;
        }
        return false;
    },
    sendDataToAPI: function (thisObject, writeFlag) {
        writeFlag = typeof writeFlag !== 'undefined' ? writeFlag : false;
        var cmdWriteFlag = writeFlag ? '--write' : '';
        var fileName = thisObject.getFileName(thisObject);
        var cmd = 'python ' +
          thisObject.apiClientLocation().replace(/(@)/g, "\\@").replace(/(\s)/g, "\\ ") +
          ' ' + cmdWriteFlag + ' --file ' + fileName + ' --plugin komodo-wakatime/'+ thisObject.VERSION +
          ' --key ' + thisObject.api_key;
        var runSvc = Components.classes["@activestate.com/koRunService;1"]
          .createInstance(Components.interfaces.koIRunService);
        var process = runSvc.RunAndNotify(cmd, '', '', '');
    },
    keyPressEvent: function (thisObject) {
        if (thisObject.enoughTimePassed(thisObject)) {
            thisObject.sendDataToAPI(thisObject);
        }
    },
    fileSaveEvent: function (thisObject) {
        thisObject.sendDataToAPI(thisObject, true);
    },
    initViewListener: function (thisObject) {
        thisObject.keyPressListener = function (event) {
            thisObject.keyPressEvent(thisObject, event);
        };
        if (thisObject.view !== null) {
            thisObject.view.removeEventListener('keypress', thisObject.keyPressListener, true);
        }
        if (ko.views.manager.currentView !== null) {
            thisObject.view = ko.views.manager.currentView;
            thisObject.view.addEventListener('keypress', thisObject.keyPressListener, true);
        }
    },
    fileChanged: function (thisObject, event) {
        thisObject.fileName = event.originalTarget.koDoc.file.displayPath;
        thisObject.keyPressListener = function (event) {
            thisObject.keyPressEvent(thisObject, event);
        };
        ko.views.manager.currentView.removeEventListener('keypress', thisObject.keyPressListener, true);
        thisObject.view = event.originalTarget;
        thisObject.view.addEventListener('keypress', thisObject.keyPressListener, true);
    }
};

window.addEventListener('file_saved', function (event) {
    komodoWakatime.fileSaveEvent(komodoWakatime, event);
}, false);
window.addEventListener('current_view_changed', function (event) {
    komodoWakatime.fileChanged(komodoWakatime, event);
}, true);
window.addEventListener('komodo-ui-started', function (event) {
    komodoWakatime.onLoad(komodoWakatime, event);
}, true);
<|endoftext|>


<commit_before>/*! http://mths.be/contains v0.1.0 by @mathias */
if (!String.prototype.contains) {
	(function() {
		'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
		var indexOf = ''.indexOf;
		String.prototype.contains = function(search) {
			if (this == null) {
				throw TypeError();
			}
			var string = String(this);
			var stringLength = string.length;
			var searchString = String(search);
			var searchLength = searchString.length;
			var position = arguments.length > 1 ? arguments[1] : undefined;
			// `ToInteger`
			var pos = position ? Number(position) : 0;
			if (isNaN(pos)) {
				pos = 0;
			}
			var start = Math.min(Math.max(pos, 0), stringLength);
			// Avoid the `indexOf` call if no match is possible
			if (searchLength + start > stringLength) {
				return false;
			}
			return indexOf.call(string, searchString, pos) != -1;
		};
	}());
}
<commit_msg>Avoid `isNaN`<commit_after>/*! http://mths.be/contains v0.1.0 by @mathias */
if (!String.prototype.contains) {
	(function() {
		'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
		var indexOf = ''.indexOf;
		String.prototype.contains = function(search) {
			if (this == null) {
				throw TypeError();
			}
			var string = String(this);
			var stringLength = string.length;
			var searchString = String(search);
			var searchLength = searchString.length;
			var position = arguments.length > 1 ? arguments[1] : undefined;
			// `ToInteger`
			var pos = position ? Number(position) : 0;
			if (pos != pos) { // better `isNaN`
				pos = 0;
			}
			var start = Math.min(Math.max(pos, 0), stringLength);
			// Avoid the `indexOf` call if no match is possible
			if (searchLength + start > stringLength) {
				return false;
			}
			return indexOf.call(string, searchString, pos) != -1;
		};
	}());
}
<|endoftext|>