<commit_before>/**
 * Inspect Controller
 * @author Lawrence Daniels <lawrence.daniels@gmail.com>
 */
(function () {
    angular.module('trifecta')
        .controller('InspectCtrl', ['$scope', '$interval', '$log', '$parse', '$timeout', 'MessageSvc', 'MessageSearchSvc', 'TopicSvc',
            function ($scope, $interval, $log, $parse, $timeout, MessageSvc, MessageSearchSvc, TopicSvc) {

                var _lastGoodResult = "";

                $scope.hideEmptyTopics = true;
                $scope.replicas = [];
                $scope.topics = [];
                $scope.topic = null;
                $scope.loading = 0;

                $scope.displayMode = {
                    "state" : "message"
                };

                $scope.clearMessage = function() {
                    $scope.message = {};
                };

                /**
                 * Converts the given offset from a string value to an integer
                 * @param partition the partition that the offset value will be updated within
                 * @param offset the given offset string value
                 */
                $scope.convertOffsetToInt = function(partition, offset) {
                    partition.offset = parseInt(offset);
                };

                /**
                 * Exports the given message to an external system
                 * @param topic the given topic
                 * @param partition the given partition
                 * @param offset the given offset
                 */
                $scope.exportMessage = function(topic, partition, offset) {
                    alert("Not yet implemented");
                };

                $scope.getMessageData = function (topic, partition, offset) {
                    $scope.clearMessage();
                    $scope.loading++;

                    MessageSvc.getMessage(topic, partition, offset).then(
                        function (message) {
                            $scope.message = message;
                            if($scope.loading) $scope.loading--;
                        },
                        function (err) {
                            $scope.addError(err);
                            if($scope.loading) $scope.loading--;
                        });
                };

                $scope.getMessageKey = function (topic, partition, offset) {
                    $scope.clearMessage();
                    $scope.loading++;

                    MessageSvc.getMessageKey(topic, partition, offset).then(
                        function (message) {
                            $scope.message = message;
                            if($scope.loading) $scope.loading--;
                        },
                        function (err) {
                            $scope.addError(err);
                            if($scope.loading) $scope.loading--;
                        });
                };

                $scope.messageFinderPopup = function () {
                    MessageSearchSvc.finderDialog($scope).then(function (form) {
                        // perform the validation of the form
                        if(!form || !form.topic) {
                            $scope.addErrorMessage("No topic selected")
                        }
                        else if(!form.criteria) {
                            $scope.addErrorMessage("No criteria specified")
                        }
                        else {
                            form.topic = form.topic.topic;
                            if (form.topic && form.criteria) {
                                // display the loading dialog
                                var loadingDialog = MessageSearchSvc.loadingDialog($scope);

                                // perform the search
                                MessageSvc.findOne(form.topic, form.criteria)
                                    .then(function (message) {
                                        loadingDialog.close({});
                                        if (message.type == "error") {
                                            $scope.addErrorMessage(message.message);
                                        }
                                        else {
                                            $scope.message = message;

                                            // find the topic and partition
                                            var myTopic = findTopicByName(message.topic);
                                            if (myTopic) {
                                                var myPartition = findPartitionByID(myTopic, message.partition);
                                                if (myPartition) {
                                                    $scope.topic = myTopic;
                                                    $scope.partition = myPartition;
                                                    $scope.partition.offset = message.offset
                                                }
                                            }
                                        }
                                    });
                            }
                        }
                    });
                };

                $scope.getReplicas = function (topic) {
                    TopicSvc.getReplicas(topic).then(
                        function (replicas) {
                            $scope.replicas = replicas;
                        },
                        function (err) {
                            $scope.addError(err);
                        });
                };

                $scope.getTopics = function (hideEmptyTopics) {
                    return $scope.topics.filter(function (topic) {
                        return !hideEmptyTopics || topic.totalMessages > 0;
                    });
                };

                $scope.loadMessage = function () {
                    var topic = $scope.topic.topic;
                    var partition = $scope.partition.partition;
                    var offset = $scope.partition.offset;

                    switch($scope.displayMode.state) {
                        case "key":
                            $scope.getMessageKey(topic, partition, offset);
                            break;
                        case "message":
                            $scope.getMessageData(topic, partition, offset);
                            break;
                        default:
                            $log.error("Unrecognized display mode (mode = " + mode + ")");
                    }
                };

                $scope.firstMessage = function () {
                    ensureOffset($scope.partition);
                    if ($scope.partition.offset != $scope.partition.startOffset) {
                        $scope.partition.offset = $scope.partition.startOffset;
                        $scope.loadMessage();
                    }
                };

                $scope.lastMessage = function () {
                    ensureOffset($scope.partition);
                    if ($scope.partition.offset != $scope.partition.endOffset) {
                        $scope.partition.offset = $scope.partition.endOffset;
                        $scope.loadMessage();
                    }
                };

                $scope.medianMessage = function() {
                    var partition = $scope.partition;
                    ensureOffset(partition);
                    var median = Math.round(partition.startOffset + (partition.endOffset - partition.startOffset)/2);
                    if (partition.offset != median) {
                        partition.offset = median;
                        $scope.loadMessage();
                    }
                };

                $scope.nextMessage = function () {
                    ensureOffset($scope.partition);
                    var offset = $scope.partition.offset;
                    if (offset < 1 + $scope.partition.endOffset) {
                        $scope.partition.offset += 1;
                        $scope.loadMessage();
                    }
                };

                $scope.previousMessage = function () {
                    ensureOffset($scope.partition);
                    var offset = $scope.partition.offset;
                    if (offset && offset > $scope.partition.startOffset) {
                        $scope.partition.offset -= 1;
                        $scope.loadMessage();
                    }
                };

                $scope.resetMessageState = function(mode, topic, partition, offset) {
                    $log.info("mode = " + mode + ", topic = " + topic + ", partition = " + partition + ", offset = " + offset);
                    switch(mode) {
                        case "key":
                            $scope.getMessageKey(topic, partition, offset);
                            break;
                        case "message":
                            $scope.getMessageData(topic, partition, offset);
                            break;
                        default:
                            $log.error("Unrecognized display mode (mode = " + mode + ")");
                    }
                };

                $scope.switchToMessage = function (topicID, partitionID, offset) {
                    $log.info("switchToMessage: topicID = " + topicID + ", partitionID = " + partitionID + ", offset = " + offset);
                    var topic = findTopicByName(topicID);
                    var partition = topic ? findPartitionByID(topic, partitionID) : null;
                    if (partition) {
                        $scope.topic = topic;
                        $scope.partition = partition;
                        $scope.partition.offset = offset;
                        $scope.loadMessage();
                        $scope.changeTab(1, null); // Query
                    }
                };

                /**
                 * Formats a JSON object as a color-coded JSON expression
                 * @param objStr the JSON object
                 * @param tabWidth the number of tabs to use in formatting
                 * @returns {*}
                 */
                $scope.toPrettyJSON = function (objStr, tabWidth) {
                    var obj = null;
                    try {
                        obj = $parse(objStr)({});
                    } catch (e) {
                        $log.error(e);
                        return _lastGoodResult;
                    }

                    var result = JSON.stringify(obj, null, Number(tabWidth));
                    _lastGoodResult = result;

                    return result;
                };

                $scope.updatePartition = function (partition) {
                    $scope.partition = partition;

                    // if the current offset is not set, set it at the starting offset.
                    ensureOffset(partition);

                    // load the first message
                    if ($scope.topic.totalMessages > 0 && $scope.partition.offset) {
                        $scope.loadMessage();
                    }
                    else {
                        $scope.clearMessage();
                    }
                };

                $scope.updateTopic = function (topic) {
                    $scope.topic = topic;

                    // asynchronously load the replicas for the topic
                    var myTopicName = topic ? topic.topic : null;
                    if(myTopicName) {
                        $scope.getReplicas(myTopicName);
                    }

                    var partitions = topic != null ? topic.partitions : null;
                    if (partitions) {
                        $scope.updatePartition(partitions[0]);
                    }
                    else {
                        console.log("No partitions found");
                        $scope.partition = {};
                        $scope.clearMessage();
                    }
                };

                function ensureOffset(partition) {
                    if(!partition.offset) {
                        partition.offset = partition.startOffset;
                    }
                }

                /**
                 * Attempts to find and return the first non-empty topic; however, if none are found, it returns the
                 * first topic in the array
                 * @param topicSummaries the given array of topic summaries
                 * @returns the first non-empty topic
                 */
                function findNonEmptyTopic(topicSummaries) {
                    for (var n = 0; n < topicSummaries.length; n++) {
                        var ts = topicSummaries[n];
                        if (ts.totalMessages > 0) return ts;
                    }
                    return topicSummaries.length > 0 ? topicSummaries[0] : null;
                }

                function findPartitionByID(topic, partitionId) {
                    var partitions = topic.partitions;
                    for (var n = 0; n < partitions.length; n++) {
                        if (partitions[n].partition == partitionId) return partitions[n];
                    }
                    return null;
                }

                function findTopicByName(topicId) {
                    var topics = $scope.topics;
                    for (var n = 0; n < topics.length; n++) {
                        if (topics[n].topic == topicId) return topics[n];
                    }
                    return null;
                }

                // initially, clear the message
                $scope.clearMessage();

                /**
                 * Watch for topic changes, and when one occurs:
                 * 1. select the first non-empty topic
                 * 2. load the replicas for the topic
                 */
                $scope.$watch("Topics.topics", function(newTopics, oldTopics) {
                    $log.info("DashboardCtrl: Loaded new topics (" + newTopics.length + ")");
                    $scope.topics = newTopics;

                    var myTopic = findNonEmptyTopic($scope.topics);
                    $scope.updateTopic(myTopic);
                });

            }]);

})();<commit_msg>fixed the issue preventing switching to the Inspect tab to display a message<commit_after>/**
 * Inspect Controller
 * @author Lawrence Daniels <lawrence.daniels@gmail.com>
 */
(function () {
    angular.module('trifecta')
        .controller('InspectCtrl', ['$scope', '$interval', '$log', '$parse', '$timeout', 'MessageSvc', 'MessageSearchSvc', 'TopicSvc',
            function ($scope, $interval, $log, $parse, $timeout, MessageSvc, MessageSearchSvc, TopicSvc) {

                var _lastGoodResult = "";

                $scope.hideEmptyTopics = true;
                $scope.replicas = [];
                $scope.topics = [];
                $scope.topic = null;
                $scope.loading = 0;

                $scope.displayMode = {
                    "state" : "message"
                };

                $scope.clearMessage = function() {
                    $scope.message = {};
                };

                /**
                 * Converts the given offset from a string value to an integer
                 * @param partition the partition that the offset value will be updated within
                 * @param offset the given offset string value
                 */
                $scope.convertOffsetToInt = function(partition, offset) {
                    partition.offset = parseInt(offset);
                };

                /**
                 * Exports the given message to an external system
                 * @param topic the given topic
                 * @param partition the given partition
                 * @param offset the given offset
                 */
                $scope.exportMessage = function(topic, partition, offset) {
                    alert("Not yet implemented");
                };

                $scope.getMessageData = function (topic, partition, offset) {
                    $scope.clearMessage();
                    $scope.loading++;

                    MessageSvc.getMessage(topic, partition, offset).then(
                        function (message) {
                            $scope.message = message;
                            if($scope.loading) $scope.loading--;
                        },
                        function (err) {
                            $scope.addError(err);
                            if($scope.loading) $scope.loading--;
                        });
                };

                $scope.getMessageKey = function (topic, partition, offset) {
                    $scope.clearMessage();
                    $scope.loading++;

                    MessageSvc.getMessageKey(topic, partition, offset).then(
                        function (message) {
                            $scope.message = message;
                            if($scope.loading) $scope.loading--;
                        },
                        function (err) {
                            $scope.addError(err);
                            if($scope.loading) $scope.loading--;
                        });
                };

                $scope.messageFinderPopup = function () {
                    MessageSearchSvc.finderDialog($scope).then(function (form) {
                        // perform the validation of the form
                        if(!form || !form.topic) {
                            $scope.addErrorMessage("No topic selected")
                        }
                        else if(!form.criteria) {
                            $scope.addErrorMessage("No criteria specified")
                        }
                        else {
                            form.topic = form.topic.topic;
                            if (form.topic && form.criteria) {
                                // display the loading dialog
                                var loadingDialog = MessageSearchSvc.loadingDialog($scope);

                                // perform the search
                                MessageSvc.findOne(form.topic, form.criteria)
                                    .then(function (message) {
                                        loadingDialog.close({});
                                        if (message.type == "error") {
                                            $scope.addErrorMessage(message.message);
                                        }
                                        else {
                                            $scope.message = message;

                                            // find the topic and partition
                                            var myTopic = findTopicByName(message.topic);
                                            if (myTopic) {
                                                var myPartition = findPartitionByID(myTopic, message.partition);
                                                if (myPartition) {
                                                    $scope.topic = myTopic;
                                                    $scope.partition = myPartition;
                                                    $scope.partition.offset = message.offset
                                                }
                                            }
                                        }
                                    });
                            }
                        }
                    });
                };

                $scope.getReplicas = function (topic) {
                    TopicSvc.getReplicas(topic).then(
                        function (replicas) {
                            $scope.replicas = replicas;
                        },
                        function (err) {
                            $scope.addError(err);
                        });
                };

                $scope.getTopics = function (hideEmptyTopics) {
                    return $scope.topics.filter(function (topic) {
                        return !hideEmptyTopics || topic.totalMessages > 0;
                    });
                };

                $scope.loadMessage = function () {
                    var topic = $scope.topic.topic;
                    var partition = $scope.partition.partition;
                    var offset = $scope.partition.offset;

                    switch($scope.displayMode.state) {
                        case "key":
                            $scope.getMessageKey(topic, partition, offset);
                            break;
                        case "message":
                            $scope.getMessageData(topic, partition, offset);
                            break;
                        default:
                            $log.error("Unrecognized display mode (mode = " + mode + ")");
                    }
                };

                $scope.firstMessage = function () {
                    ensureOffset($scope.partition);
                    if ($scope.partition.offset != $scope.partition.startOffset) {
                        $scope.partition.offset = $scope.partition.startOffset;
                        $scope.loadMessage();
                    }
                };

                $scope.lastMessage = function () {
                    ensureOffset($scope.partition);
                    if ($scope.partition.offset != $scope.partition.endOffset) {
                        $scope.partition.offset = $scope.partition.endOffset;
                        $scope.loadMessage();
                    }
                };

                $scope.medianMessage = function() {
                    var partition = $scope.partition;
                    ensureOffset(partition);
                    var median = Math.round(partition.startOffset + (partition.endOffset - partition.startOffset)/2);
                    if (partition.offset != median) {
                        partition.offset = median;
                        $scope.loadMessage();
                    }
                };

                $scope.nextMessage = function () {
                    ensureOffset($scope.partition);
                    var offset = $scope.partition.offset;
                    if (offset < 1 + $scope.partition.endOffset) {
                        $scope.partition.offset += 1;
                        $scope.loadMessage();
                    }
                };

                $scope.previousMessage = function () {
                    ensureOffset($scope.partition);
                    var offset = $scope.partition.offset;
                    if (offset && offset > $scope.partition.startOffset) {
                        $scope.partition.offset -= 1;
                        $scope.loadMessage();
                    }
                };

                $scope.resetMessageState = function(mode, topic, partition, offset) {
                    $log.info("mode = " + mode + ", topic = " + topic + ", partition = " + partition + ", offset = " + offset);
                    switch(mode) {
                        case "key":
                            $scope.getMessageKey(topic, partition, offset);
                            break;
                        case "message":
                            $scope.getMessageData(topic, partition, offset);
                            break;
                        default:
                            $log.error("Unrecognized display mode (mode = " + mode + ")");
                    }
                };

                $scope.switchToMessage = function (topicID, partitionID, offset) {
                    $log.info("switchToMessage: topicID = " + topicID + ", partitionID = " + partitionID + ", offset = " + offset);
                    var topic = findTopicByName(topicID);
                    var partition = topic ? findPartitionByID(topic, partitionID) : null;
                    if (partition) {
                        $scope.topic = topic;
                        $scope.partition = partition;
                        $scope.partition.offset = offset;
                        $scope.loadMessage();
                        $scope.changeTab(0, null); // Inspect
                    }
                };

                /**
                 * Formats a JSON object as a color-coded JSON expression
                 * @param objStr the JSON object
                 * @param tabWidth the number of tabs to use in formatting
                 * @returns {*}
                 */
                $scope.toPrettyJSON = function (objStr, tabWidth) {
                    var obj = null;
                    try {
                        obj = $parse(objStr)({});
                    } catch (e) {
                        $log.error(e);
                        return _lastGoodResult;
                    }

                    var result = JSON.stringify(obj, null, Number(tabWidth));
                    _lastGoodResult = result;

                    return result;
                };

                $scope.updatePartition = function (partition) {
                    $scope.partition = partition;

                    // if the current offset is not set, set it at the starting offset.
                    ensureOffset(partition);

                    // load the first message
                    if ($scope.topic.totalMessages > 0 && $scope.partition.offset) {
                        $scope.loadMessage();
                    }
                    else {
                        $scope.clearMessage();
                    }
                };

                $scope.updateTopic = function (topic) {
                    $scope.topic = topic;

                    // asynchronously load the replicas for the topic
                    var myTopicName = topic ? topic.topic : null;
                    if(myTopicName) {
                        $scope.getReplicas(myTopicName);
                    }

                    var partitions = topic != null ? topic.partitions : null;
                    if (partitions) {
                        $scope.updatePartition(partitions[0]);
                    }
                    else {
                        console.log("No partitions found");
                        $scope.partition = {};
                        $scope.clearMessage();
                    }
                };

                function ensureOffset(partition) {
                    if(!partition.offset) {
                        partition.offset = partition.startOffset;
                    }
                }

                /**
                 * Attempts to find and return the first non-empty topic; however, if none are found, it returns the
                 * first topic in the array
                 * @param topicSummaries the given array of topic summaries
                 * @returns the first non-empty topic
                 */
                function findNonEmptyTopic(topicSummaries) {
                    for (var n = 0; n < topicSummaries.length; n++) {
                        var ts = topicSummaries[n];
                        if (ts.totalMessages > 0) return ts;
                    }
                    return topicSummaries.length > 0 ? topicSummaries[0] : null;
                }

                function findPartitionByID(topic, partitionId) {
                    var partitions = topic.partitions;
                    for (var n = 0; n < partitions.length; n++) {
                        if (partitions[n].partition == partitionId) return partitions[n];
                    }
                    return null;
                }

                function findTopicByName(topicId) {
                    var topics = $scope.topics;
                    for (var n = 0; n < topics.length; n++) {
                        if (topics[n].topic == topicId) return topics[n];
                    }
                    return null;
                }

                // initially, clear the message
                $scope.clearMessage();

                /**
                 * Watch for topic changes, and when one occurs:
                 * 1. select the first non-empty topic
                 * 2. load the replicas for the topic
                 */
                $scope.$watch("Topics.topics", function(newTopics, oldTopics) {
                    $log.info("DashboardCtrl: Loaded new topics (" + newTopics.length + ")");
                    $scope.topics = newTopics;

                    var myTopic = findNonEmptyTopic($scope.topics);
                    $scope.updateTopic(myTopic);
                });

            }]);

})();<|endoftext|>


<commit_before>var sys           = require('sys'),
    http          = require('http'),
    URL           = require('url'),
    HtmlToDom     = require('./htmltodom').HtmlToDom,
    domToHtml     = require('./domtohtml').domToHtml,
    htmlencoding  = require('./htmlencoding'),
    HTMLEncode    = htmlencoding.HTMLEncode,
    HTMLDecode    = htmlencoding.HTMLDecode,
    jsdom         = require('../../jsdom'),
    vm            = require('vm');

function NOT_IMPLEMENTED(target) {
  return function() {
    if (!jsdom.debugMode) {
      var trigger = target ? target.trigger : this.trigger;
      trigger.call(this, 'error', 'NOT IMPLEMENTED');
    }
  };
}

/**
 * Creates a window having a document. The document can be passed as option,
 * if omitted, a new document will be created.
 */
exports.windowAugmentation = function(dom, options) {
  options = options || {};
  var window = exports.createWindow(dom, options);

  if (!options.document) {
    var browser = browserAugmentation(dom, options);

    if (options.features && options.features.QuerySelector) {
      require(__dirname + "/../selectors/index").applyQuerySelectorPrototype(browser);
    }

    options.document = (browser.HTMLDocument)             ?
                        new browser.HTMLDocument(options) :
                        new browser.Document(options);

    options.document.write('');
  }

  var doc = window.document = options.document;

  if (doc.addEventListener) {
    if (doc.readyState == 'complete') {
      var ev = doc.createEvent('HTMLEvents');
      ev.initEvent('load', false, false);
      window.dispatchEvent(ev);
    }
    else {
      doc.addEventListener('load', function(ev) {
        window.dispatchEvent(ev);
      });
    }
  }

  return window;
};

/**
 * Creates a document-less window.
 */
exports.createWindow = function(dom, options) {
  var timers = [];

  function startTimer(startFn, stopFn, callback, ms) {
	  var res = startFn(callback, ms);
	  timers.push( [ res, stopFn ] );
	  return res;
  }

  function stopTimer(id) {
	  if (typeof id === 'undefined') {
		  return;
	  }
	  for (var i in timers) {
		  if (timers[i][0] === id) {
			  timers[i][1].call(this, id);
			  timers.splice(i, 1);
			  break;
		  }
	  }
  }

  function stopAllTimers() {
	  timers.forEach(function (t) {
		  t[1].call(this, t[0]);
	  });
	  timers = [];
  }

  function DOMWindow(options) {
    this.frames = [this];
    this.contentWindow = this;
    this.window = this.self = this.parent = this.top = this;

    var href = (options || {}).url || 'file://' + __filename;
    this.location = URL.parse(href);
    this.location.reload = NOT_IMPLEMENTED(this);
    this.location.replace = NOT_IMPLEMENTED(this);
    this.location.toString = function() {
      return href;
    };

    var window = this.console._window = this;

    if (options && options.document) {
      options.document.location = this.location;
    }
    this.addEventListener = function() {
      dom.Node.prototype.addEventListener.apply(window, arguments);
    };
    this.removeEventListener = function() {
      dom.Node.prototype.removeEventListener.apply(window, arguments);
    };
    this.dispatchEvent = function() {
      dom.Node.prototype.dispatchEvent.apply(window, arguments);
    };
    this.trigger = function(){
      dom.Node.prototype.trigger.apply(window.document, arguments);
    };

    this.setTimeout = function (fn, ms) { return startTimer(setTimeout, clearTimeout, fn, ms); };
    this.setInterval = function (fn, ms) { return startTimer(setInterval, clearInterval, fn, ms); };
    this.clearInterval = stopTimer;
    this.clearTimeout = stopTimer;
    this.__stopAllTimers = stopAllTimers;

  }

  DOMWindow.prototype = {
    __proto__: dom,
    close : function() {
      if (this.document) {
        if (this.document.body) {
          this.document.body.innerHTML = "";
        }

        if (this.document.close) {
          this.document.close();
        }
        delete this.document;
      }

      stopAllTimers();

      this.window = this.self = this.parent = this.top = null;
    },
    getComputedStyle: function(node) {
      var s = node.style,
          cs = {};

      for (var n in s) {
        cs[n] = s[n];
      }
      cs.__proto__ = {
        getPropertyValue: function(name) {
          return node.style[name];
        }
      };
      return cs;
    },
    console: {
      log:   function(message) { this._window.trigger('log',   message) },
      info:  function(message) { this._window.trigger('info',  message) },
      warn:  function(message) { this._window.trigger('warn',  message) },
      error: function(message) { this._window.trigger('error', message) }
    },
    navigator: {
      userAgent: 'Node.js (' + process.platform + '; U; rv:' + process.version + ')',
      appName: 'Node.js jsDom',
      platform: process.platform,
      appVersion: process.version
    },
    XMLHttpRequest: function XMLHttpRequest() {},

    name: 'nodejs',
    innerWidth: 1024,
    innerHeight: 768,
    length: 1,
    outerWidth: 1024,
    outerHeight: 768,
    pageXOffset: 0,
    pageYOffset: 0,
    screenX: 0,
    screenY: 0,
    screenLeft: 0,
    screenTop: 0,
    scrollX: 0,
    scrollY: 0,
    scrollTop: 0,
    scrollLeft: 0,
    alert: NOT_IMPLEMENTED(),
    blur: NOT_IMPLEMENTED(),
    confirm: NOT_IMPLEMENTED(),
    createPopup: NOT_IMPLEMENTED(),
    focus: NOT_IMPLEMENTED(),
    moveBy: NOT_IMPLEMENTED(),
    moveTo: NOT_IMPLEMENTED(),
    open: NOT_IMPLEMENTED(),
    print: NOT_IMPLEMENTED(),
    prompt: NOT_IMPLEMENTED(),
    resizeBy: NOT_IMPLEMENTED(),
    resizeTo: NOT_IMPLEMENTED(),
    scroll: NOT_IMPLEMENTED(),
    scrollBy: NOT_IMPLEMENTED(),
    scrollTo: NOT_IMPLEMENTED(),
    screen : {
      width : 0,
      height : 0
    },
    Image : NOT_IMPLEMENTED()
  };

  var window = new DOMWindow(options);

  // This is the last chance we have to properly update the window
  // so turn it into a context now.
  window = vm.createContext(window);
  return window;
};

//Caching for HTMLParser require. HUGE performace boost.
/**
* 5000 iterations
* Without cache: ~1800+ms
* With cache: ~80ms
*/
var defaultParser = null;
function getDefaultParser() {
  if (defaultParser === null) {
    try {
      defaultParser = require('htmlparser');
    }
    catch (e) {
      try {
        defaultParser = require('node-htmlparser/lib/node-htmlparser');
      }
      catch (e2) {
        defaultParser = undefined;
      }
    }
  }
  return defaultParser;
}

/**
 * Augments the given DOM by adding browser-specific properties and methods (BOM).
 * Returns the augmented DOM.
 */
var browserAugmentation = exports.browserAugmentation = function(dom, options) {

  if (dom._augmented) {
    return dom;
  }

  if(!options) {
    options = {};
  }

  // set up html parser - use a provided one or try and load from library
  var htmltodom = new HtmlToDom(options.parser || getDefaultParser());

  if (!dom.HTMLDocument) {
    dom.HTMLDocument = dom.Document;
  }
  if (!dom.HTMLDocument.prototype.write) {
    dom.HTMLDocument.prototype.write = function(html) {
      this.innerHTML = html;
    };
  }

  dom.Element.prototype.getElementsByClassName = function(className) {

    function filterByClassName(child) {
      if (!child) {
        return false;
      }

      if (child.nodeType &&
          child.nodeType === dom.Node.ENTITY_REFERENCE_NODE)
      {
        child = child._entity;
      }

      var classString = child.className;
      if (classString) {
        var s = classString.split(" ");
        for (var i=0; i<s.length; i++) {
          if (s[i] === className) {
            return true;
          }
        }
      }
      return false;
    }

    return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName));
  };

  dom.Element.prototype.__defineGetter__('sourceIndex', function() {
    /*
    * According to QuirksMode:
    * Get the sourceIndex of element x. This is also the index number for
    * the element in the document.getElementsByTagName('*') array.
    * http://www.quirksmode.org/dom/w3c_core.html#t77
    */
    var items = this.ownerDocument.getElementsByTagName('*'),
        len = items.length;

    for (var i = 0; i < len; i++) {
      if (items[i] === this) {
        return i;
      }
    }
  });

  dom.Document.prototype.__defineGetter__('outerHTML', function() {
    return domToHtml(this);
  });

  dom.Element.prototype.__defineGetter__('outerHTML', function() {
    return domToHtml(this);
  });

  dom.Element.prototype.__defineGetter__('innerHTML', function() {
    return domToHtml(this._childNodes, true);
  });
  dom.Element.prototype.__defineSetter__('doctype', function() {
    throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR);
  });
  dom.Element.prototype.__defineGetter__('doctype', function() {
    var r = null;
    if (this.nodeName == '#document') {
        if (this._doctype) {
         r = this._doctype;
        }
    }
    return r;
  });

  dom.Element.prototype.__defineSetter__('innerHTML', function(html) {
    //Check for lib first

    if (html === null) {
      return null;
    }

    //Clear the children first:
    var child;
    while ((child = this._childNodes[0])) {
      this.removeChild(child);
    }

    if (this.nodeName === '#document') {
      parseDocType(this, html);
    }
    var nodes = htmltodom.appendHtmlToElement(html, this);
    return html;
  });

  dom.Document.prototype.__defineGetter__('innerHTML', function() {
    return domToHtml(this._childNodes, true);
  });

  dom.Document.prototype.__defineSetter__('innerHTML', function(html) {
    //Check for lib first

    if (html === null) {
      return null;
    }

    //Clear the children first:
    var child;
    while ((child = this._childNodes[0])) {
      this.removeChild(child);
    }

    if (this.nodeName === '#document') {
      parseDocType(this, html);
    }
    var nodes = htmltodom.appendHtmlToElement(html, this);
    return html;
  });

  var DOC_HTML5      = /<!doctype html>/i,
      DOC_TYPE       = /<!DOCTYPE (\w(.|\n)*)">/i;
      DOC_TYPE_START = '<!DOCTYPE ',
      DOC_TYPE_END   = '">';

  function parseDocType(doc, html) {
    var publicID = '',
        systemID = '',
        fullDT = '',
        name = 'HTML',
        set = true,
        doctype = html.match(DOC_HTML5);

    //Default, No doctype === null
    doc._doctype = null;

    if (doctype && doctype[0]) { //Handle the HTML shorty doctype
      fullDT = doctype[0];
    } else { //Parse the doctype
      // find the start
      var start     = html.indexOf(DOC_TYPE_START),
          end       = html.indexOf(DOC_TYPE_END),
          docString;

      if (start < 0 || end < 0) {
        return;
      }

      docString = html.substr(start, (end-start)+DOC_TYPE_END.length);
      doctype = docString.replace(/[\n\r]/g,'').match(DOC_TYPE);

      if (!doctype) {
        return;
      }

      fullDT = doctype[0];
      doctype = doctype[1].split(' "');
      var _id1 = doctype.length ? doctype.pop().replace(/"/g, '') : '',
          _id2 = doctype.length ? doctype.pop().replace(/"/g, '') : '';

      if (_id1.indexOf('-//') !== -1) {
        publicID = _id1;
      }
      if (_id2.indexOf('-//') !== -1) {
        publicID = _id2;
      }
      if (_id1.indexOf('://') !== -1) {
        systemID = _id1;
      }
      if (_id2.indexOf('://') !== -1) {
        systemID = _id2;
      }
      if (doctype.length) {
        doctype = doctype[0].split(' ');
        name = doctype[0].toUpperCase();
      }
    }
    doc._doctype = new dom.DOMImplementation().createDocumentType(name, publicID, systemID);
    doc._doctype._ownerDocument = doc;
    doc._doctype._fullDT = fullDT;
    doc._doctype.toString = function() {
      return this._fullDT;
    };
  }

  dom.Document.prototype.getElementsByClassName = function(className) {

    function filterByClassName(child) {
      if (!child) {
        return false;
      }

      if (child.nodeType &&
          child.nodeType === dom.Node.ENTITY_REFERENCE_NODE)
      {
        child = child._entity;
      }

      var classString = child.className;
      if (classString) {
        var s = classString.split(" ");
        for (var i=0; i<s.length; i++) {
          if (s[i] === className) {
            return true;
          }
        }
      }
      return false;
    }

    return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName));
  };

  dom.Element.prototype.__defineGetter__('nodeName', function(val) {
    return this._nodeName.toUpperCase();
  });

  dom.Element.prototype.__defineGetter__('tagName', function(val) {
    var t = this._tagName.toUpperCase();
    //Document should not return a tagName
    if (this.nodeName === '#document') {
      t = null;
    }
    return t;
  });

  dom.Element.prototype.scrollTop = 0;
  dom.Element.prototype.scrollLeft = 0;

  dom.Document.prototype.__defineGetter__('parentWindow', function() {
    if (!this._parentWindow) {
      this._parentWindow = exports.windowAugmentation(dom, {document: this, url: this.URL});
    }
    return this._parentWindow;
  });

  dom.Document.prototype.__defineSetter__('parentWindow', function(window) {
    this._parentWindow = window;
  });

  dom.Document.prototype.__defineGetter__('defaultView', function() {
    return this.parentWindow;
  });

  dom._augmented = true;
  return dom;
};
<commit_msg>browser/index.js: Changed semicolon to comma in var list (DOC_TYPE_START and DOC_TYPE_END were globals by accident).<commit_after>var sys           = require('sys'),
    http          = require('http'),
    URL           = require('url'),
    HtmlToDom     = require('./htmltodom').HtmlToDom,
    domToHtml     = require('./domtohtml').domToHtml,
    htmlencoding  = require('./htmlencoding'),
    HTMLEncode    = htmlencoding.HTMLEncode,
    HTMLDecode    = htmlencoding.HTMLDecode,
    jsdom         = require('../../jsdom'),
    vm            = require('vm');

function NOT_IMPLEMENTED(target) {
  return function() {
    if (!jsdom.debugMode) {
      var trigger = target ? target.trigger : this.trigger;
      trigger.call(this, 'error', 'NOT IMPLEMENTED');
    }
  };
}

/**
 * Creates a window having a document. The document can be passed as option,
 * if omitted, a new document will be created.
 */
exports.windowAugmentation = function(dom, options) {
  options = options || {};
  var window = exports.createWindow(dom, options);

  if (!options.document) {
    var browser = browserAugmentation(dom, options);

    if (options.features && options.features.QuerySelector) {
      require(__dirname + "/../selectors/index").applyQuerySelectorPrototype(browser);
    }

    options.document = (browser.HTMLDocument)             ?
                        new browser.HTMLDocument(options) :
                        new browser.Document(options);

    options.document.write('');
  }

  var doc = window.document = options.document;

  if (doc.addEventListener) {
    if (doc.readyState == 'complete') {
      var ev = doc.createEvent('HTMLEvents');
      ev.initEvent('load', false, false);
      window.dispatchEvent(ev);
    }
    else {
      doc.addEventListener('load', function(ev) {
        window.dispatchEvent(ev);
      });
    }
  }

  return window;
};

/**
 * Creates a document-less window.
 */
exports.createWindow = function(dom, options) {
  var timers = [];

  function startTimer(startFn, stopFn, callback, ms) {
	  var res = startFn(callback, ms);
	  timers.push( [ res, stopFn ] );
	  return res;
  }

  function stopTimer(id) {
	  if (typeof id === 'undefined') {
		  return;
	  }
	  for (var i in timers) {
		  if (timers[i][0] === id) {
			  timers[i][1].call(this, id);
			  timers.splice(i, 1);
			  break;
		  }
	  }
  }

  function stopAllTimers() {
	  timers.forEach(function (t) {
		  t[1].call(this, t[0]);
	  });
	  timers = [];
  }

  function DOMWindow(options) {
    this.frames = [this];
    this.contentWindow = this;
    this.window = this.self = this.parent = this.top = this;

    var href = (options || {}).url || 'file://' + __filename;
    this.location = URL.parse(href);
    this.location.reload = NOT_IMPLEMENTED(this);
    this.location.replace = NOT_IMPLEMENTED(this);
    this.location.toString = function() {
      return href;
    };

    var window = this.console._window = this;

    if (options && options.document) {
      options.document.location = this.location;
    }
    this.addEventListener = function() {
      dom.Node.prototype.addEventListener.apply(window, arguments);
    };
    this.removeEventListener = function() {
      dom.Node.prototype.removeEventListener.apply(window, arguments);
    };
    this.dispatchEvent = function() {
      dom.Node.prototype.dispatchEvent.apply(window, arguments);
    };
    this.trigger = function(){
      dom.Node.prototype.trigger.apply(window.document, arguments);
    };

    this.setTimeout = function (fn, ms) { return startTimer(setTimeout, clearTimeout, fn, ms); };
    this.setInterval = function (fn, ms) { return startTimer(setInterval, clearInterval, fn, ms); };
    this.clearInterval = stopTimer;
    this.clearTimeout = stopTimer;
    this.__stopAllTimers = stopAllTimers;

  }

  DOMWindow.prototype = {
    __proto__: dom,
    close : function() {
      if (this.document) {
        if (this.document.body) {
          this.document.body.innerHTML = "";
        }

        if (this.document.close) {
          this.document.close();
        }
        delete this.document;
      }

      stopAllTimers();

      this.window = this.self = this.parent = this.top = null;
    },
    getComputedStyle: function(node) {
      var s = node.style,
          cs = {};

      for (var n in s) {
        cs[n] = s[n];
      }
      cs.__proto__ = {
        getPropertyValue: function(name) {
          return node.style[name];
        }
      };
      return cs;
    },
    console: {
      log:   function(message) { this._window.trigger('log',   message) },
      info:  function(message) { this._window.trigger('info',  message) },
      warn:  function(message) { this._window.trigger('warn',  message) },
      error: function(message) { this._window.trigger('error', message) }
    },
    navigator: {
      userAgent: 'Node.js (' + process.platform + '; U; rv:' + process.version + ')',
      appName: 'Node.js jsDom',
      platform: process.platform,
      appVersion: process.version
    },
    XMLHttpRequest: function XMLHttpRequest() {},

    name: 'nodejs',
    innerWidth: 1024,
    innerHeight: 768,
    length: 1,
    outerWidth: 1024,
    outerHeight: 768,
    pageXOffset: 0,
    pageYOffset: 0,
    screenX: 0,
    screenY: 0,
    screenLeft: 0,
    screenTop: 0,
    scrollX: 0,
    scrollY: 0,
    scrollTop: 0,
    scrollLeft: 0,
    alert: NOT_IMPLEMENTED(),
    blur: NOT_IMPLEMENTED(),
    confirm: NOT_IMPLEMENTED(),
    createPopup: NOT_IMPLEMENTED(),
    focus: NOT_IMPLEMENTED(),
    moveBy: NOT_IMPLEMENTED(),
    moveTo: NOT_IMPLEMENTED(),
    open: NOT_IMPLEMENTED(),
    print: NOT_IMPLEMENTED(),
    prompt: NOT_IMPLEMENTED(),
    resizeBy: NOT_IMPLEMENTED(),
    resizeTo: NOT_IMPLEMENTED(),
    scroll: NOT_IMPLEMENTED(),
    scrollBy: NOT_IMPLEMENTED(),
    scrollTo: NOT_IMPLEMENTED(),
    screen : {
      width : 0,
      height : 0
    },
    Image : NOT_IMPLEMENTED()
  };

  var window = new DOMWindow(options);

  // This is the last chance we have to properly update the window
  // so turn it into a context now.
  window = vm.createContext(window);
  return window;
};

//Caching for HTMLParser require. HUGE performace boost.
/**
* 5000 iterations
* Without cache: ~1800+ms
* With cache: ~80ms
*/
var defaultParser = null;
function getDefaultParser() {
  if (defaultParser === null) {
    try {
      defaultParser = require('htmlparser');
    }
    catch (e) {
      try {
        defaultParser = require('node-htmlparser/lib/node-htmlparser');
      }
      catch (e2) {
        defaultParser = undefined;
      }
    }
  }
  return defaultParser;
}

/**
 * Augments the given DOM by adding browser-specific properties and methods (BOM).
 * Returns the augmented DOM.
 */
var browserAugmentation = exports.browserAugmentation = function(dom, options) {

  if (dom._augmented) {
    return dom;
  }

  if(!options) {
    options = {};
  }

  // set up html parser - use a provided one or try and load from library
  var htmltodom = new HtmlToDom(options.parser || getDefaultParser());

  if (!dom.HTMLDocument) {
    dom.HTMLDocument = dom.Document;
  }
  if (!dom.HTMLDocument.prototype.write) {
    dom.HTMLDocument.prototype.write = function(html) {
      this.innerHTML = html;
    };
  }

  dom.Element.prototype.getElementsByClassName = function(className) {

    function filterByClassName(child) {
      if (!child) {
        return false;
      }

      if (child.nodeType &&
          child.nodeType === dom.Node.ENTITY_REFERENCE_NODE)
      {
        child = child._entity;
      }

      var classString = child.className;
      if (classString) {
        var s = classString.split(" ");
        for (var i=0; i<s.length; i++) {
          if (s[i] === className) {
            return true;
          }
        }
      }
      return false;
    }

    return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName));
  };

  dom.Element.prototype.__defineGetter__('sourceIndex', function() {
    /*
    * According to QuirksMode:
    * Get the sourceIndex of element x. This is also the index number for
    * the element in the document.getElementsByTagName('*') array.
    * http://www.quirksmode.org/dom/w3c_core.html#t77
    */
    var items = this.ownerDocument.getElementsByTagName('*'),
        len = items.length;

    for (var i = 0; i < len; i++) {
      if (items[i] === this) {
        return i;
      }
    }
  });

  dom.Document.prototype.__defineGetter__('outerHTML', function() {
    return domToHtml(this);
  });

  dom.Element.prototype.__defineGetter__('outerHTML', function() {
    return domToHtml(this);
  });

  dom.Element.prototype.__defineGetter__('innerHTML', function() {
    return domToHtml(this._childNodes, true);
  });
  dom.Element.prototype.__defineSetter__('doctype', function() {
    throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR);
  });
  dom.Element.prototype.__defineGetter__('doctype', function() {
    var r = null;
    if (this.nodeName == '#document') {
        if (this._doctype) {
         r = this._doctype;
        }
    }
    return r;
  });

  dom.Element.prototype.__defineSetter__('innerHTML', function(html) {
    //Check for lib first

    if (html === null) {
      return null;
    }

    //Clear the children first:
    var child;
    while ((child = this._childNodes[0])) {
      this.removeChild(child);
    }

    if (this.nodeName === '#document') {
      parseDocType(this, html);
    }
    var nodes = htmltodom.appendHtmlToElement(html, this);
    return html;
  });

  dom.Document.prototype.__defineGetter__('innerHTML', function() {
    return domToHtml(this._childNodes, true);
  });

  dom.Document.prototype.__defineSetter__('innerHTML', function(html) {
    //Check for lib first

    if (html === null) {
      return null;
    }

    //Clear the children first:
    var child;
    while ((child = this._childNodes[0])) {
      this.removeChild(child);
    }

    if (this.nodeName === '#document') {
      parseDocType(this, html);
    }
    var nodes = htmltodom.appendHtmlToElement(html, this);
    return html;
  });

  var DOC_HTML5      = /<!doctype html>/i,
      DOC_TYPE       = /<!DOCTYPE (\w(.|\n)*)">/i,
      DOC_TYPE_START = '<!DOCTYPE ',
      DOC_TYPE_END   = '">';

  function parseDocType(doc, html) {
    var publicID = '',
        systemID = '',
        fullDT = '',
        name = 'HTML',
        set = true,
        doctype = html.match(DOC_HTML5);

    //Default, No doctype === null
    doc._doctype = null;

    if (doctype && doctype[0]) { //Handle the HTML shorty doctype
      fullDT = doctype[0];
    } else { //Parse the doctype
      // find the start
      var start     = html.indexOf(DOC_TYPE_START),
          end       = html.indexOf(DOC_TYPE_END),
          docString;

      if (start < 0 || end < 0) {
        return;
      }

      docString = html.substr(start, (end-start)+DOC_TYPE_END.length);
      doctype = docString.replace(/[\n\r]/g,'').match(DOC_TYPE);

      if (!doctype) {
        return;
      }

      fullDT = doctype[0];
      doctype = doctype[1].split(' "');
      var _id1 = doctype.length ? doctype.pop().replace(/"/g, '') : '',
          _id2 = doctype.length ? doctype.pop().replace(/"/g, '') : '';

      if (_id1.indexOf('-//') !== -1) {
        publicID = _id1;
      }
      if (_id2.indexOf('-//') !== -1) {
        publicID = _id2;
      }
      if (_id1.indexOf('://') !== -1) {
        systemID = _id1;
      }
      if (_id2.indexOf('://') !== -1) {
        systemID = _id2;
      }
      if (doctype.length) {
        doctype = doctype[0].split(' ');
        name = doctype[0].toUpperCase();
      }
    }
    doc._doctype = new dom.DOMImplementation().createDocumentType(name, publicID, systemID);
    doc._doctype._ownerDocument = doc;
    doc._doctype._fullDT = fullDT;
    doc._doctype.toString = function() {
      return this._fullDT;
    };
  }

  dom.Document.prototype.getElementsByClassName = function(className) {

    function filterByClassName(child) {
      if (!child) {
        return false;
      }

      if (child.nodeType &&
          child.nodeType === dom.Node.ENTITY_REFERENCE_NODE)
      {
        child = child._entity;
      }

      var classString = child.className;
      if (classString) {
        var s = classString.split(" ");
        for (var i=0; i<s.length; i++) {
          if (s[i] === className) {
            return true;
          }
        }
      }
      return false;
    }

    return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName));
  };

  dom.Element.prototype.__defineGetter__('nodeName', function(val) {
    return this._nodeName.toUpperCase();
  });

  dom.Element.prototype.__defineGetter__('tagName', function(val) {
    var t = this._tagName.toUpperCase();
    //Document should not return a tagName
    if (this.nodeName === '#document') {
      t = null;
    }
    return t;
  });

  dom.Element.prototype.scrollTop = 0;
  dom.Element.prototype.scrollLeft = 0;

  dom.Document.prototype.__defineGetter__('parentWindow', function() {
    if (!this._parentWindow) {
      this._parentWindow = exports.windowAugmentation(dom, {document: this, url: this.URL});
    }
    return this._parentWindow;
  });

  dom.Document.prototype.__defineSetter__('parentWindow', function(window) {
    this._parentWindow = window;
  });

  dom.Document.prototype.__defineGetter__('defaultView', function() {
    return this.parentWindow;
  });

  dom._augmented = true;
  return dom;
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name           AfterDawn download wrapper remover
// @version        0.5
// @author         Bruno Barbieri
// @description    Rewrites the download link to remove the download wrapper in AfterDawn (i.e. "Safe Download" button)
// @include        http://afterdawn.*/*
// @include        http://*.afterdawn.*/*
// @include        http://www.*.afterdawn.*/*
// @include        http://download.fi/*
// @include        http://*.download.fi/*
// @include        http://www.*.download.fi/*
// @namespace      http://userscripts.org/scripts/show/161313
// @updateURL      http://userscripts.org/scripts/source/161313.meta.js
// @downloadURL    http://userscripts.org/scripts/source/161313.user.js
// ==/UserScript==

/*
 * ===== Changelog =====
 *
 * v0.1: Initial version
 * v0.2: Switch code to jQuery and only keep one regex search, just in case
 * v0.3: Use alternate method for loading jQuery, now works with Chrome as well. Credits to Erik Vold (http://r.evold.ca/jquery4us).
 *       Use domain wildcards that work in Chrome as well, and add the Finnish domain.
 * v0.4: Added update URL and updated JQuery version
 * v0.5: Pure JavaScript implementation (http://youmightnotneedjquery.com)
 *
 */

function main() {
    // Remove extra text from button (e.g. "Safe Download")
    downBtn = document.querySelectorAll('#download-main-button')[0];
    safeDownHtml = document.querySelectorAll('#download-main-button > b')[0].innerHTML;
    downBtnHtml = downBtn.innerHTML.replace('' + safeDownHtml + '', '');
    downBtn.innerHTML = downBtnHtml;

    // Remove extra style from button
    className = 'extra-label';
    if (downBtn.classList) {
        downBtn.classList.remove(className)
    } else {
        downBtn.className = downBtn.className.replace(new RegExp('(^| )' + className.split(' ').join('|') + '( |$)', 'gi'), ' ');
    }

    // Remove hidden input
    hiddenInput = document.querySelectorAll('input[name="installer"]')[0];
    hiddenInput.parentNode.removeChild(hiddenInput);

    // Replace installer option in button URL
    downBtnUrl = downBtn.getAttribute('href').replace(/installer=[\d]+/, 'installer=0');
    downBtn.setAttribute('href', downBtnUrl)
}

main();
<commit_msg>Modified   AfterDawn/afterdawn_download_wrapper_remover.user.js<commit_after>// ==UserScript==
// @name           AfterDawn download wrapper remover
// @version        0.5.1
// @author         Bruno Barbieri
// @description    Rewrites the download link to remove the download wrapper in AfterDawn (i.e. "Safe Download" button)
// @include        http://afterdawn.*/*
// @include        http://*.afterdawn.*/*
// @include        http://www.*.afterdawn.*/*
// @include        http://download.fi/*
// @include        http://*.download.fi/*
// @include        http://www.*.download.fi/*
// @namespace      http://userscripts.org/scripts/show/161313
// @updateURL      http://userscripts.org/scripts/source/161313.meta.js
// @downloadURL    http://userscripts.org/scripts/source/161313.user.js
// ==/UserScript==

/*
 * ===== Changelog =====
 *
 * v0.1: Initial version
 * v0.2: Switch code to jQuery and only keep one regex search, just in case
 * v0.3: Use alternate method for loading jQuery, now works with Chrome as well. Credits to Erik Vold (http://r.evold.ca/jquery4us).
 *       Use domain wildcards that work in Chrome as well, and add the Finnish domain.
 * v0.4: Added update URL and updated JQuery version
 * v0.5: Pure JavaScript implementation (http://youmightnotneedjquery.com)
 *
 */

function main() {
    // Remove extra text from button (e.g. "Safe Download")
    downBtn = document.getElementById('download-main-button');
    safeDownHtml = document.querySelectorAll('#download-main-button > b')[0].innerHTML;
    downBtnHtml = downBtn.innerHTML.replace('' + safeDownHtml + '', '');
    downBtn.innerHTML = downBtnHtml;

    // Remove extra style from button
    className = 'extra-label';
    if (downBtn.classList) {
        downBtn.classList.remove(className)
    } else {
        downBtn.className = downBtn.className.replace(new RegExp('(^| )' + className.split(' ').join('|') + '( |$)', 'gi'), ' ');
    }

    // Remove hidden input
    hiddenInput = document.querySelectorAll('input[name="installer"]')[0];
    hiddenInput.parentNode.removeChild(hiddenInput);

    // Replace installer option in button URL
    downBtnUrl = downBtn.getAttribute('href').replace(/installer=[\d]+/, 'installer=0');
    downBtn.setAttribute('href', downBtnUrl)
}

main();
<|endoftext|>


<commit_before>var cheerio = require("cheerio");
var request = require('request');
var fs = require('fs');

function doRequest(year, dir, url, csv) {
	request(url, function(err, resp, data){
		if (!err) {
			//console.log(data);
			var $ = cheerio.load(data);

			csv += "date,vistor,vistorAbrv,vistorGoals,home,homeAbrv,homeGoals,notes";
			$("#div_games table tr").each(function() {
				var count = 0;
				$('td',this).each(function(index,element) {
					count++;
					if($(this).attr("align") != ("center")) {
						if(index == 5)
							csv += '"' + $(this).text() + '"';
						else {
							if(index == 6 && $(this).text() != '')
								csv += '"' + $(this).text() + '"';
							else if(index != 6)
								csv += '"' + $(this).text() + '"' + ',';
						}
						if(index == 1 || index == 3) {
							var re = /\/([A-Z]*)\//g;
							var match = re.exec($("a",this).attr("href"));
							csv += '"' + match[1] + '"' + ",";
						}
					}
				});
				csv += "\n";
			});

			if (!fs.existsSync(dir)){
				fs.mkdirSync(dir);
			}

			fs.writeFile(dir + "results.csv", csv, function(err) {
				if(err) {
					console.log(err);
				} else {
					console.log("The file was saved!");
				}
			});
		}
	});
}

function getResults() {
	var year = 1917;
	var dir = "";
	var url = "";
	var csv = "";
	while (year < 2015) {
		dir = "../../data/" + year + "/";
		url = "http://www.hockey-reference.com/leagues/NHL_"+ (year+1) +"_games.html";
		csv = "";

		doRequest(year, dir, url, csv);
		year++;
	}
}

getResults();<commit_msg>Scraper now scrapes player data too<commit_after>//2015-03-08
//Copyright: Justin Visser
var cheerio = require("cheerio");
var request = require('request');
var fs = require('fs');

function doResultsRequest(year, dir, url, csv) {
	request(url, function(err, resp, data){
		if (!err) {
			//console.log(data);
			var $ = cheerio.load(data);

			csv += "date,vistor,vistorAbrv,vistorGoals,home,homeAbrv,homeGoals,notes";
			$("#div_games table tr").each(function() {
				$('td',this).each(function(index,element) {
					if($(this).attr("align") != ("center")) {
						if(index == 5)
							csv += '"' + $(this).text() + '"';
						else {
							if(index == 6 && $(this).text() != '')
								csv += '"' + $(this).text() + '"';
							else if(index != 6)
								csv += '"' + $(this).text() + '"' + ',';
						}
						if(index == 1 || index == 3) {
							var re = /\/([A-Z]*)\//g;
							var match = re.exec($("a",this).attr("href"));
							csv += '"' + match[1] + '"' + ",";
						}
					}
				});
				csv += "\n";
			});

			if (!fs.existsSync(dir)){
				fs.mkdirSync(dir);
			}

			fs.writeFile(dir + "results.csv", csv, function(err) {
				if(err) {
					console.log(err);
				} else {
					console.log("The file was saved!");
				}
			});
		}
	});
}

function doPlayersRequest(year, dir, url, csv) {
	request(url, function(err, resp, data){
		if (!err) {
			//console.log(data);
			var $ = cheerio.load(data);

			csv += "rank,player,age,teamName,position,gamesPlayed,goals,assists,points,plusMinus,penaltyMinutes,evenStrengthGoals,powerPlayGoals,shortHandedGoals,gameWinningPoints,evenStrengthAssists,powerPlayAssits,shortHandedAssists,shots,shootingPercentage,timeOnIce,averageTimeOnIce\n";
			$("#div_stats table tbody tr").each(function() {
				//Only take if not one of the headings.
				if(!($(this).hasClass("no_ranker"))) {
					$('td',this).each(function(index,element) {
						if(index >= 21)
							var field = '"' + $(this).text().replace('*','') + '"';
						else
							var field = '"' + $(this).text().replace('*','') + '"' +',';
						csv += field;
					});
					csv += "\n";
				}
			});
			if (!fs.existsSync(dir)){
				fs.mkdirSync(dir);
			}

			fs.writeFile(dir + "players.csv", csv, function(err) {
				if(err) {
					console.log(err);
				} else {
					console.log("The file was saved!");
				}
			});
		}
	});
}

function getResults() {
	var year = 1917;
	var dir = "";
	var url = "";
	var csv = "";
	while (year < 2015) {
		dir = "../../data/" + year + "/";
		url = "http://www.hockey-reference.com/leagues/NHL_"+ (year+1) +"_games.html";

		csv = "";
		doResultsRequest(year, dir, url, csv);
		url = "http://www.hockey-reference.com/leagues/NHL_"+ (year+1) +"_skaters.html";
		csv = "";
		doPlayersRequest(year, dir, url, csv);

		year++;
	}
}

function testing() {
	var year = 1917;
	var dir = "";
	var url = "";
	var csv = "";

	dir = "../../data/" + year + "/";
	url = "http://www.hockey-reference.com/leagues/NHL_"+ (year+1) +"_skaters.html";
	csv = "";
	doPlayersRequest(year, dir, url, csv);

	year++;
}

//testing();
getResults();<|endoftext|>


<commit_before>var Util = require("./utils.js")

var PublishApp = function() {
  var util = new Util();
  this.runScript = function(client, userOptions) {

    util.init(client, userOptions);

    util.loginAndWait();

    util.step("Go to application page", function() {

    }, function() {
      util.action("Click on element", ['a', [userOptions.title, userOptions.title + " " + userOptions.version]);
    });

    util.step("Wait for app page to load", function() {
      client.waitFor('ol', util.TIMEOUT, util.onTimeout("Wait for app page to load"));
    }, function() {
      util.action("Click on element", ['a', ['Re-publish this app', 'Publish this app']]);
    });

    util.step("Wait for publishing to complete", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for publishing to complete"));
    }, function() {

    });
  }

  return this;
}

exports = module.exports = PublishApp;
<commit_msg>missing bracket<commit_after>var Util = require("./utils.js")

var PublishApp = function() {
  var util = new Util();
  this.runScript = function(client, userOptions) {

    util.init(client, userOptions);

    util.loginAndWait();

    util.step("Go to application page", function() {

    }, function() {
      util.action("Click on element", ['a', [userOptions.title, userOptions.title + " " + userOptions.version]]);
    });

    util.step("Wait for app page to load", function() {
      client.waitFor('ol', util.TIMEOUT, util.onTimeout("Wait for app page to load"));
    }, function() {
      util.action("Click on element", ['a', ['Re-publish this app', 'Publish this app']]);
    });

    util.step("Wait for publishing to complete", function() {
      client.waitFor('div[data-notification-type="INFO"][aria-hidden="false"]', util.TIMEOUT, util.onTimeout("Wait for publishing to complete"));
    }, function() {

    });
  }

  return this;
}

exports = module.exports = PublishApp;
<|endoftext|>


<commit_before>(function (controllers, undefined) {

    /**
     * @ngdoc controller
     * @name Merchello.Dashboards.Report.SalesByItemController
     * @function
     *
     * @description
     * The controller for the reports SalesByItem page
     */
    controllers.SalesByItemController = function ($scope, $element, assetsService, angularHelper, merchelloPluginReportSalesByItemService) {

        $scope.loaded = false;
        $scope.preValuesLoaded = true;
        $scope.results = [];
        $scope.itemsPerPage = 0;
        $scope.totalItems = 0;
        $scope.filterStartDate = '';
        $scope.filterEndDate = '';

        /**
         * @ngdoc method
         * @name setupDatePicker
         * @function
         *
         * @description
         * Sets up the datepickers
        */
        $scope.setupDatePicker = function (pickerId) {

            // Open the datepicker and add a changeDate eventlistener
            $element.find(pickerId).datetimepicker();

            //Ensure to remove the event handler when this instance is destroyted
            $scope.$on('$destroy', function () {
                $element.find(pickerId).datetimepicker("destroy");
            });
        };

        assetsService.loadCss('/App_Plugins/Merchello/Common/Css/merchello.css');
        assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css').then(function () {
            var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.min.js"];
            assetsService.load(filesToLoad).then(
                function () {
                    //The Datepicker js and css files are available and all components are ready to use.
                    $scope.setupDatePicker("#filterStartDate");
                    $element.find("#filterStartDate").datetimepicker().on("changeDate", $scope.applyDateStart);

                    $scope.setupDatePicker("#filterEndDate");
                    $element.find("#filterEndDate").datetimepicker().on("changeDate", $scope.applyDateEnd);
                });
        });

        //handles the date changing via the api
        $scope.applyDateStart = function (e) {
            angularHelper.safeApply($scope, function () {
                // when a date is changed, update the model
                if (e.localDate) {
                    $scope.filterStartDate = e.localDate.toIsoDateString();
                }
            });
        }

        //handles the date changing via the api
        $scope.applyDateEnd = function (e) {
            angularHelper.safeApply($scope, function () {
                // when a date is changed, update the model
                if (e.localDate) {
                    $scope.filterEndDate = e.localDate.toIsoDateString();
                }
            });
        }

        $scope.setDefaultDates = function(actual) {
            var month = actual.getMonth() == 0 ? 11 : actual.getMonth() - 1;

            var start = new Date(actual.getFullYear(), month, actual.getDay());
            $scope.applyDateStart(start.toIsoDateString());
            $scope.applyDateEnd(actual.toIsoDateString());
        }

        $scope.defaultData = function () {

            var promise = merchelloPluginReportSalesByItemService.getDefaultData();
            promise.then(function (data) {
                $scope.loaded = true;
                $scope.results = _.map(data.items, function(resultFromServer) {
                    return new merchello.Models.SaleByItemResult(resultFromServer, true);
                });
                $scope.itemsPerPage = data.itemsPerPage;
                $scope.totalItems = data.totalItems;
            });

        };

        $scope.init = function () {
            $scope.setDefaultDates(new Date());
            $scope.defaultData();
            $scope.loaded = true;
        };

        $scope.init();
    };

    angular.module("umbraco").controller("Merchello.Plugins.Reports.SalesByItemController", ['$scope', '$element', 'assetsService', 'angularHelper', 'merchelloPluginReportSalesByItemService', merchello.Controllers.SalesByItemController]);

}(window.merchello.Controllers = window.merchello.Controllers || {}));
<commit_msg>Initial work on report date filters.<commit_after>(function (controllers, undefined) {

    /**
     * @ngdoc controller
     * @name Merchello.Dashboards.Report.SalesByItemController
     * @function
     *
     * @description
     * The controller for the reports SalesByItem page
     */
    controllers.SalesByItemController = function ($scope, $element, assetsService, angularHelper, merchelloPluginReportSalesByItemService) {

        $scope.loaded = false;
        $scope.preValuesLoaded = true;
        $scope.results = [];
        $scope.itemsPerPage = 0;
        $scope.totalItems = 0;
        $scope.filterStartDate = '';
        $scope.filterEndDate = '';

        /**
         * @ngdoc method
         * @name setupDatePicker
         * @function
         *
         * @description
         * Sets up the datepickers
        */
        $scope.setupDatePicker = function (pickerId) {

            // Open the datepicker and add a changeDate eventlistener
            $element.find(pickerId).datetimepicker();

            //Ensure to remove the event handler when this instance is destroyted
            $scope.$on('$destroy', function () {
                $element.find(pickerId).datetimepicker("destroy");
            });
        };

        assetsService.loadCss('/App_Plugins/Merchello/Common/Css/merchello.css');
        assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css').then(function () {
            var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.min.js"];
            assetsService.load(filesToLoad).then(
                function () {
                    //The Datepicker js and css files are available and all components are ready to use.
                    $scope.setupDatePicker("#filterStartDate");
                    $element.find("#filterStartDate").datetimepicker().on("changeDate", $scope.applyDateStart);

                    $scope.setupDatePicker("#filterEndDate");
                    $element.find("#filterEndDate").datetimepicker().on("changeDate", $scope.applyDateEnd);
                });
        });

        //handles the date changing via the api
        $scope.applyDateStart = function (e) {
            angularHelper.safeApply($scope, function () {
                // when a date is changed, update the model
                if (e.localDate) {
                    $scope.filterStartDate = e.localDate.toIsoDateString();
                }
            });
        }

        //handles the date changing via the api
        $scope.applyDateEnd = function (e) {
            angularHelper.safeApply($scope, function () {
                // when a date is changed, update the model
                if (e.localDate) {
                    $scope.filterEndDate = e.localDate.toIsoDateString();
                }
            });
        }

        $scope.setDefaultDates = function(actual) {
            var month = actual.getMonth() == 0 ? 11 : actual.getMonth() - 1;

            var start = new Date(actual.getFullYear(), month, actual.getDay());
            $scope.applyDateStart(start);
            $scope.applyDateEnd(actual);
        }

        $scope.defaultData = function () {

            var promise = merchelloPluginReportSalesByItemService.getDefaultData();
            promise.then(function (data) {
                $scope.loaded = true;
                $scope.results = _.map(data.items, function(resultFromServer) {
                    return new merchello.Models.SaleByItemResult(resultFromServer, true);
                });
                $scope.itemsPerPage = data.itemsPerPage;
                $scope.totalItems = data.totalItems;
            });

        };

        $scope.init = function () {
            $scope.setDefaultDates(new Date());
            $scope.defaultData();
            $scope.loaded = true;
        };

        $scope.init();
    };

    angular.module("umbraco").controller("Merchello.Plugins.Reports.SalesByItemController", ['$scope', '$element', 'assetsService', 'angularHelper', 'merchelloPluginReportSalesByItemService', merchello.Controllers.SalesByItemController]);

}(window.merchello.Controllers = window.merchello.Controllers || {}));
<|endoftext|>


<commit_before>// ==UserScript==
// @name OGInvoice
// @namespace https://github.com/momocow/OGInvoice
// @description OGame: Trade Tracker
// @version 2.1.6
// @author MomoCow
// @supportURL https://github.com/momocow/OGInvoice/issues
// @updateURL https://gist.githubusercontent.com/momocow/bf932d571dcad386193224ecd6e86d5c/raw/OGInvoice.js
// @include https://*.ogame.gameforge.com/game/index.php?*
// @run-at document-end
// ==/UserScript==

(function(){
    'use strict';

    //Script Object
    function OGInv() {
		//DATA
        this.calQueue = [];
        this.info = {name: "OGInvoice", version: "2.1.6", author: "MomoCow", site: "https://github.com/momocow", description: "OGame: 自動追蹤/統計 交易資源量", statistic:[], storage: []};

		//init
		var sloaded = JSON.parse(localStorage.getItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage'));
        var cloaded = JSON.parse(localStorage.getItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic'));
		if(sloaded){
            this.info.storage = sloaded;
        }
        if(cloaded){
            this.info.statistic = cloaded;
        }

		//METHOD
        this.toString = function(){
            return JSON.stringify(this.info);
        };

		this.update = function(){
			var v = localStorage.getItem('oginv_version');
			if(v && v !== this.info.version){
				this.reset();
				localStorage.setItem('oginv_version', this.info.version);
			}
			else if(!v){
				localStorage.setItem('oginv_version', this.info.version);
			}
		};

		this.reset = function(){
			localStorage.removeItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic');
			localStorage.removeItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage');
            this.info.storage = [];
            this.info.statistic = [];
			this.refreshPanel();
		};

        this.res2int = function(str){
            return parseInt(str.replace(/,/g, ''));
        };

        this.int2res = function(int){
            var str = "";
            if(int === 0) return '0';
            else if(int < 0) return 'error: negative resources';
            else while(int > 0){
                if(str !== ""){
                    str = "," + str;
                }
                var tmp_num = (int % 1000).toString();
                int = Math.floor(int / 1000);
                while(tmp_num.length < 3 && int !== 0){
                    tmp_num = '0' + tmp_num;
                }
                str = tmp_num + str;
            }
            return str;
        };

        this.push = function(item){
            for(var id in this.info.storage){
                if(item.is(this.info.storage[id])){
                    return this;
                }
            }

            this.calQueue.push(item);
            this.info.storage.push(item);
            return this;
        };

        this.pop = function(){
            this.storage.pop();
            return this;
        };

        this.save = function(){
            if($(this.calQueue).size()>0){
                localStorage.setItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic', JSON.stringify(this.info.statistic));
                localStorage.setItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage', JSON.stringify(this.info.storage));
                this.calQueue = [];
            }
            return this;
        };

        this.calculate = function(){
            if($(this.calQueue).size()>0){
                var now = $('.OGameClock').text().split(" ");
                for(var sidx in this.calQueue){
                    var logged = false;
                    for(var cidx in this.info.statistic){
                        if(this.info.statistic[cidx].info.src == this.calQueue[sidx].info.src){
                            logged = true;
                            this.info.statistic[cidx].info.name = this.calQueue[sidx].info.name;
                            this.info.statistic[cidx].info.date = now[0];
                            this.info.statistic[cidx].info.time = now[1];
                            this.info.statistic[cidx].info.metal = this.int2res(this.res2int(this.info.statistic[cidx].info.metal) + this.res2int(this.calQueue[sidx].info.metal));
                            this.info.statistic[cidx].info.crystal = this.int2res(this.res2int(this.info.statistic[cidx].info.crystal) + this.res2int(this.calQueue[sidx].info.crystal));
                            this.info.statistic[cidx].info.deut = this.int2res(this.res2int(this.info.statistic[cidx].info.deut) + this.res2int(this.calQueue[sidx].info.deut));
                        }
                    }
                    if(!logged) this.info.statistic.push(new Invoice(this.calQueue[sidx].info.src, now[0], now[1], this.calQueue[sidx].info.src, this.calQueue[sidx].info.name, undefined, undefined, this.calQueue[sidx].info.metal, this.calQueue[sidx].info.crystal, this.calQueue[sidx].info.deut, undefined));
                }
            }

            return this;
        };

        this.record = function(txt){
            $(txt).find('.msg').each(function(id, m){
                var invoice_pattern = /由外來艦隊運送的資源\s(\d\d\.\d\d\.\d\d\d\d)\s(\d\d\:\d\d\:\d\d)\s來自\:\s太空監測\s來自\s(.+)\s\([^\[]+\[(\d\:\d{1,3}:\d{1,2})\]\)\s的一支艦隊正運送著資源到\s.+\s\[(\d\:\d{1,3}\:\d{1,2})\]\s\:金屬\:\s([\d,]+)\s單位,晶體\:\s([\d,]+)\s單位,重氫\:\s([\d,]+)\s單位/;
                var clrStr = $(m).text().replace(/(\s)+/g,'$1');
                var abstract = invoice_pattern.exec(clrStr);
                if(abstract){
                    var invoice = new Invoice($(m).data('msgId'), abstract[1], abstract[2], $($(m).find('.player').attr('title').replace(/玩家\:[^\|]*\|/, '')).find('.sendMail.tooltip').data('playerid'), abstract[3], abstract[4], abstract[5], abstract[6], abstract[7], abstract[8], $(m).html());
                    oginv.push(invoice);
                }
            });

            return this;
        };

		this.player = function(name){
			return $(this.playerLib).find('player[name=\'' + name + '\']').attr('id');
		};

		this.refreshPanel = function(){
			$(".oginv_data").remove();
			for(var idx in this.info.statistic){
                $('#oginv_info_total').append('

'+this.info.statistic[idx].info.name+'

'+this.info.statistic[idx].info.metal+'

'+this.info.statistic[idx].info.crystal+'

'+this.info.statistic[idx].info.deut+'

'+this.info.statistic[idx].info.date+' '+this.info.statistic[idx].info.time+'

');
            }
            //re-styling
            $('.oginv_data, .oginv_data_title').css({'display':'table-row'});
            $('.oginv_field').css({'display':'table-cell', 'padding':'7px 22px 7px 22px'});
            $('.oginv_data, .oginv_field').css({'border': '1px solid #6f6f6f'});
		};

        this.showPanel = function(){
            $('#menuTable').append('

交易統計
');
            $('#oginv_btn_info').on('click', function (){
                if($('#contentWrapper').css('display') == 'none'){
                    $('#contentWrapper').css('display', 'block');
                    $('#oginv_page').remove();
                    $("#oginv_btn_setting").removeClass("active");
                }
                else{
                    $("#oginv_btn_setting").addClass("active");

                    //DOM contructing
                    $('head').append('<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/components/button.min.css">');
                    $('#contentWrapper').after('

交易統計

累計交易量

玩家

金屬

晶體

重氫

更新時間

重設所有資料

重設

');
                    //show statistic
                    oginv.refreshPanel();

                    //styling
                    $('#oginv_page').css({'width':"670px", 'overflow':"visible", 'position':"relative", 'padding':"0 0 25px 0",'float':"left"});
                    $('#oginv_info_banner').css({"background-image":'url(https://' + location.host + '/headerCache/resources/ice_1_2_3_4_212.jpg)', "background-repeat":"no-repeat", "height":"300px", "margin":"0 auto 3px auto", "position":"relative", "width":"654px"});
                    $('#oginv_info_banner h2').css({"color":"#fff", "font":"bold 18px/22px Verdana,Arial,Helvetica,sans-serif", "height":"22px", "margin":"0 0 0 144px", "overflow":"hidden", "padding-top":"7px", "white-space":"nowrap", "width":"470px", "text-overflow":"ellipsis"});
                    $('#contentWrapper').css('display', 'none');
                    $('.oginv_row').css({"background":"0", "min-height":"150px", "margin":"0 auto 1px auto", "width":"670px"});
                    $('.oginv_label').css({"height":"28px", "position":"relative", "background-image":"url(//gf1.geo.gfsrv.net/cdn63/10e31cd5234445e4084558ea3506ea.gif)", "background-repeat":"no-repeat"});
                    $('.oginv_label h2').css({"text-align":"center", "color":"#6f9fc8", "font":"700 12px/28px Verdana,Arial,Helvetica,sans-serif"});
                    $('.oginv_content').css({"background":"url(//gf1.geo.gfsrv.net/cdn03/db530b4ddcbe680361a6f837ce0dd7.gif) repeat-y", "margin":"0", "min-height":"115px", "padding":"10px 0", "position":"relative", "text-align":"center"});
                    $('#oginv_info_total').css({'display':'table', 'margin':'auto', 'border-collapse': 'collapse','border': '1px solid #6f6f6f'});

					//event
					$('#oginv_btn_reset_all').on('click', function(){oginv.reset();});

                }});
            $('#oginv_img_setting').css('background', 'transparent url(https://i.imgur.com/PkGTYyO.png) no-repeat 0 0')
                                 .css('height', '27px')
                                 .css('width', '27px')
                                 .hover(function (){
                                     $(this).css('background-position', '0 -54px');
                                   }, function (){
                                     if($('#oginv_btn_setting').hasClass('active')) $(this).css('background-position', '0 -27px');
                                     else $(this).css('background-position', '0 0');
                                   }
                                 );
        };
    }

    //Storage Item Object
    function Invoice(i, da, ti, s, n, f,to, m, c, de, r){
        this.info = {id: i, date: da, time: ti, src: s, name: n, from: f, to: to, metal: m, crystal: c, deut: de, raw: r};
        this.toString = function(){
            return JSON.stringify(this.info);
        };
        this.is = function(other){
            if(this.info.id && other.info.id) return (this.info.id === other.info.id);
            return false;
        };
    }

    //Script instance
    var oginv = new OGInv();
	oginv.update();

    if(location.href.indexOf('ogame.gameforge.com/game/index.php?page=messages') >=0){
        $(document).ajaxSuccess(function(e,d,s){
            if(s.url === "index.php?page=messages&tab=23&ajax=1" || /.*&tabid=23.*&pagination=\d+&ajax=1/.exec(s.data)){
                var tmp = $(document.createElement('div')).append(d.responseText);
                oginv.record(tmp).calculate().save();
            }
        });
	}

		oginv.showPanel();
})();
<commit_msg>Latest Trade added<commit_after>// ==UserScript==
// @name OGInvoice
// @namespace https://github.com/momocow/OGInvoice
// @description OGame: Trade Tracker
// @version 2.2.0
// @author MomoCow
// @supportURL https://github.com/momocow/OGInvoice/issues
// @updateURL https://gist.githubusercontent.com/momocow/bf932d571dcad386193224ecd6e86d5c/raw/OGInvoice.js
// @include https://*.ogame.gameforge.com/game/index.php?*
// @run-at document-end
// ==/UserScript==

(function(){
    'use strict';

    //Script Object
    function OGInv() {
		//DATA
        this.calQueue = [];
        this.info = {name: "OGInvoice", version: '2.2.0', author: "MomoCow", site: "https://github.com/momocow", description: "OGame: 自動追蹤/統計 交易資源量", statistic:[], storage: []};

		//init
		var sloaded = JSON.parse(localStorage.getItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage'));
        var cloaded = JSON.parse(localStorage.getItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic'));
		if(sloaded){
            this.info.storage = sloaded;
        }
        if(cloaded){
            this.info.statistic = cloaded;
        }

		//METHOD
        this.toString = function(){
            return JSON.stringify(this.info);
        };

		this.update = function(){
			var v = localStorage.getItem('oginv_version');
			if(v && v === this.info.version){
				return this;
			}
			localStorage.setItem('oginv_version', this.info.version);
			return this;
		};

		this.reset = function(){
			localStorage.removeItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic');
			localStorage.removeItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage');
            this.info.storage = [];
            this.info.statistic = [];
			this.refreshPanel();
		};

        this.res2int = function(str){
            return parseInt(str.replace(/,/g, ''));
        };

        this.int2res = function(int){
            var str = "";
            if(int === 0) return '0';
            else if(int < 0) return 'error: negative resources';
            else while(int > 0){
                if(str !== ""){
                    str = "," + str;
                }
                var tmp_num = (int % 1000).toString();
                int = Math.floor(int / 1000);
                while(tmp_num.length < 3 && int !== 0){
                    tmp_num = '0' + tmp_num;
                }
                str = tmp_num + str;
            }
            return str;
        };

		this.latest_datetime  = function(dtset){
			dtset = $.map(dtset, function(value, idx){
				return new Date(value.replace(/(\d{1,2})\.(\d{1,2})\.(\d{4}) (\d\d\:\d\d\:\d\d)/,'$3 $2 $1 $4'));
			});

			var latest = null;
			for(var dt_idx in dtset){
				if(latest === null || latest.getTime() < dtset[dt_idx].getTime()){
					latest = dtset[dt_idx];
				}
			}

            var rdate = '', rtime = '';
            if(latest.getDate() < 10) rdate += '0' + latest.getDate() + '.';
            else rdate += latest.getDate() + '.';
            if(latest.getMonth() < 9) rdate += '0' + (latest.getMonth() + 1) + '.';
            else rdate += (latest.getMonth() + 1) + '.';
            rdate += latest.getFullYear();

            if(latest.getHours() < 10) rtime += '0' + latest.getHours() + ':';
            else rtime += latest.getHours() + ':';
            if(latest.getMinutes() < 10) rtime += '0' + latest.getMinutes() + ':';
            else rtime += latest.getMinutes() + ':';
            if(latest.getSeconds() < 10) rtime += '0' + latest.getSeconds();
            else rtime += latest.getSeconds();
			return {date: rdate, time: rtime};
		};

        this.push = function(item){
            for(var id in this.info.storage){
                if(item.is(this.info.storage[id])){
                    return this;
                }
            }

            this.calQueue.push(item);
            this.info.storage.push(item);
            return this;
        };

        this.pop = function(){
            this.storage.pop();
            return this;
        };

        this.save = function(){
            if($(this.calQueue).size()>0){
                localStorage.setItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_statistic', JSON.stringify(this.info.statistic));
                localStorage.setItem('oginv_' + (/s\d+\-[^\.]+/.exec(location.href)) + '_' + playerId + '_storage', JSON.stringify(this.info.storage));
                this.calQueue = [];
            }
            return this;
        };

        this.calculate = function(){
            if($(this.calQueue).size()>0){
                var now = $('.OGameClock').text().split(" ");
                for(var sidx in this.calQueue){
                    var logged = false;
                    for(var cidx in this.info.statistic){
                        if(this.info.statistic[cidx].info.src == this.calQueue[sidx].info.src){
							var timestamp1 = this.info.statistic[cidx].info.date + ' ' + this.info.statistic[cidx].info.time,
							    timestamp2 = this.calQueue[sidx].info.date + ' ' + this.calQueue[sidx].info.time;
                            logged = true;
                            this.info.statistic[cidx].info.name = this.calQueue[sidx].info.name;
                            this.info.statistic[cidx].info.udate = now[0];
                            this.info.statistic[cidx].info.utime = now[1];
							this.info.statistic[cidx].info.date = this.latest_datetime([timestamp1, timestamp2]).date;
                            this.info.statistic[cidx].info.time = this.latest_datetime([timestamp1, timestamp2]).time;
                            this.info.statistic[cidx].info.metal = this.int2res(this.res2int(this.info.statistic[cidx].info.metal) + this.res2int(this.calQueue[sidx].info.metal));
                            this.info.statistic[cidx].info.crystal = this.int2res(this.res2int(this.info.statistic[cidx].info.crystal) + this.res2int(this.calQueue[sidx].info.crystal));
                            this.info.statistic[cidx].info.deut = this.int2res(this.res2int(this.info.statistic[cidx].info.deut) + this.res2int(this.calQueue[sidx].info.deut));
                        }
                    }
                    if(!logged) this.info.statistic.push(new Invoice(this.calQueue[sidx].info.src, this.calQueue[sidx].info.date, this.calQueue[sidx].info.time, this.calQueue[sidx].info.src, this.calQueue[sidx].info.name, undefined, undefined, this.calQueue[sidx].info.metal, this.calQueue[sidx].info.crystal, this.calQueue[sidx].info.deut, undefined).set('udate', now[0]).set('utime', now[1]));
                }
            }

            return this;
        };

        this.recalculate = function(){
            this.info.statistic = [];
            this.calQueue = [];
            this.calQueue = this.info.storage.slice();
            return this;
        };

        this.record = function(txt){
            $(txt).find('.msg').each(function(id, m){
                var invoice_pattern = /由外來艦隊運送的資源\s(\d\d\.\d\d\.\d\d\d\d)\s(\d\d\:\d\d\:\d\d)\s來自\:\s太空監測\s來自\s(.+)\s\([^\[]+\[(\d\:\d{1,3}:\d{1,2})\]\)\s的一支艦隊正運送著資源到\s.+\s\[(\d\:\d{1,3}\:\d{1,2})\]\s\:金屬\:\s([\d,]+)\s單位,晶體\:\s([\d,]+)\s單位,重氫\:\s([\d,]+)\s單位/;
                var clrStr = $(m).text().replace(/(\s)+/g,'$1');
                var abstract = invoice_pattern.exec(clrStr);
                if(abstract){
                    var invoice = new Invoice($(m).data('msgId'), abstract[1], abstract[2], $($(m).find('.player').attr('title').replace(/玩家\:[^\|]*\|/, '')).find('.sendMail.tooltip').data('playerid'), abstract[3], abstract[4], abstract[5], abstract[6], abstract[7], abstract[8], $(m).html());
                    oginv.push(invoice);
                }
            });

            return this;
        };

		this.refreshPanel = function(){
            $("#oginv_info_total .oginv_data").remove();
            if($(this.info.statistic).size() > 0){
			    for(var idx in this.info.statistic){
                    $('#oginv_info_total').append('

'+this.info.statistic[idx].info.name+'

'+this.info.statistic[idx].info.metal+'

'+this.info.statistic[idx].info.crystal+'

'+this.info.statistic[idx].info.deut+'

'+this.info.statistic[idx].info.date+' '+this.info.statistic[idx].info.time+'

');
                }
            }
            else{
                $('#oginv_info_total').after('
- 無資料 -
');
            }
            //re-styling
            $('.oginv_data, .oginv_data_title').css({'display':'table-row'});
            $('.oginv_field').css({'display':'table-cell', 'padding':'7px 22px 7px 22px'});
            $('#oginv_info_total .oginv_data, #oginv_info_total .oginv_field').css({'border': '1px solid #6f6f6f'});

            return this;
		};

        this.showPanel = function(){
            $('#menuTable').append('

交易統計
');
            $('#oginv_btn_info').on('click', function (){
                if($('#contentWrapper').css('display') == 'none'){
                    $('#contentWrapper').css('display', 'block');
                    $('#oginv_page').remove();
                    $("#oginv_btn_setting").removeClass("active");
                }
                else{
                    $("#oginv_btn_setting").addClass("active");

                    //DOM contructing
                    $('head').append('<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.2/components/button.min.css">');
                    $('#contentWrapper').after('

交易統計

累計交易量

玩家

金屬

晶體

重氫

最後交易時間

控制台

重新計算

重設所有資料

');
                    //show statistic
                    oginv.refreshPanel();

                    //styling
                    $('#oginv_page').css({'width':"670px", 'overflow':"visible", 'position':"relative", 'padding':"0 0 25px 0",'float':"left"});
                    $('#oginv_info_banner').css({"background-image":'url(https://' + location.host + '/headerCache/resources/ice_1_2_3_4_212.jpg)', "background-repeat":"no-repeat", "height":"300px", "margin":"0 auto 3px auto", "position":"relative", "width":"654px"});
                    $('#oginv_info_banner h2').css({"color":"#fff", "font":"bold 18px/22px Verdana,Arial,Helvetica,sans-serif", "height":"22px", "margin":"0 0 0 144px", "overflow":"hidden", "padding-top":"7px", "white-space":"nowrap", "width":"470px", "text-overflow":"ellipsis"});
                    $('#contentWrapper').css('display', 'none');
                    $('.oginv_row').css({"background":"0", "min-height":"150px", "margin":"0 auto 1px auto", "width":"670px"});
                    $('.oginv_label').css({"height":"28px", "position":"relative", "background-image":"url(//gf1.geo.gfsrv.net/cdn63/10e31cd5234445e4084558ea3506ea.gif)", "background-repeat":"no-repeat"});
                    $('.oginv_label h2').css({"text-align":"center", "color":"#6f9fc8", "font":"700 12px/28px Verdana,Arial,Helvetica,sans-serif"});
                    $('.oginv_content').css({"background":"url(//gf1.geo.gfsrv.net/cdn03/db530b4ddcbe680361a6f837ce0dd7.gif) repeat-y", "margin":"0", "min-height":"115px", "padding":"10px 0", "position":"relative", "text-align":"center"});
                    $('#oginv_info_total').css({'display':'table', 'margin':'auto', 'border-collapse': 'collapse','border': '1px solid #6f6f6f', 'max-width': '550px'});
					$('.oginv_table').css({'display':'table', 'margin':'auto', 'border-collapse': 'collapse'});

					//event
					$('#oginv_btn_recal').on('click', function(){oginv.recalculate().calculate().save().refreshPanel();});
                    $('#oginv_btn_reset_all').on('click', function(){oginv.reset();});
                }});
            $('#oginv_img_setting').css('background', 'transparent url(https://i.imgur.com/PkGTYyO.png) no-repeat 0 0')
                                 .css('height', '27px')
                                 .css('width', '27px')
                                 .hover(function (){
                                     $(this).css('background-position', '0 -54px');
                                   }, function (){
                                     if($('#oginv_btn_setting').hasClass('active')) $(this).css('background-position', '0 -27px');
                                     else $(this).css('background-position', '0 0');
                                   }
                                 );
        };
    }

    //Storage Item Object
    function Invoice(i, da, ti, s, n, f,to, m, c, de, r){
        this.info = {id: i, date: da, time: ti, src: s, name: n, from: f, to: to, metal: m, crystal: c, deut: de, raw: r};
        this.toString = function(){
            return JSON.stringify(this.info);
        };
        this.is = function(other){
            if(this.info.id && other.info.id) return (this.info.id === other.info.id);
            return false;
        };
		this.set = function(index, value){
			this.info[index] = value;
			return this;
		};
    }

    //Script instance
    var oginv = new OGInv();

    if(location.href.indexOf('ogame.gameforge.com/game/index.php?page=messages') >=0){
        $(document).ajaxSuccess(function(e,d,s){
            if(s.url === "index.php?page=messages&tab=23&ajax=1" || /.*&tabid=23.*&pagination=\d+&ajax=1/.exec(s.data)){
                var tmp = $(document.createElement('div')).append(d.responseText);
                oginv.update().record(tmp).calculate().save();
            }
        });
	}

		oginv.showPanel();
})();<|endoftext|>