<commit_before>/*
 * This script goes through your Gmail Inbox and finds recent emails where you
 * were the last respondent. It applies a nice label to them, so you can
 * see them in Priority Inbox or do something else.
 *
 * To remove and ignore an email thread, just remove the unrespondedLabel and
 * apply the ignoreLabel.
 *
 * This is most effective when paired with a time-based script trigger.
 *
 * For installation instructions, read this blog post:
 * http://jonathan-kim.com/2013/Gmail-No-Response/
 */

// Edit these to your liking.
var unrespondedLabel = 'No Response',
    ignoreLabel = 'Ignore No Response',
    minDays = 5,
    maxDays = 14;

function main() {
  processUnresponded();
  cleanUp();
}

function processUnresponded() {
  var threads = GmailApp.search('is:sent from:me -in:chats older_than:' + minDays + 'd newer_than:' + maxDays + 'd'),
      numUpdated = 0,
      minDaysAgo = new Date();

  minDaysAgo.setDate(minDaysAgo.getDate() - minDays);

  // Filter threads where I was the last respondent.
  for (var i = 0; i < threads.length; i++) {
    var thread = threads[i],
        messages = thread.getMessages(),
        lastMessage = messages[messages.length - 1],
        lastFrom = lastMessage.getFrom(),
        lastMessageIsOld = lastMessage.getDate().getTime() < minDaysAgo.getTime();

    if (fromMe(lastFrom) && lastMessageIsOld && !threadHasLabel(thread, ignoreLabel)) {
      markUnresponded(thread);
      numUpdated++;
    }
  }

  Logger.log('Updated ' + numUpdated + ' messages.');
}

function getEmailAddresses() {
  var me = Session.getActiveUser().getEmail(),
      emails = GmailApp.getAliases();

  emails.push(me);
  return emails;
}

function fromMe(fromAddress) {
  var addresses = getEmailAddresses();
  for (i = 0; i < addresses.length; i++) {
    var address = addresses[i],
        r = RegExp(address);

    if (r.test(fromAddress)) {
      return true;
    }
  }

  return false;
}

function threadHasLabel(thread, labelName) {
  var labels = thread.getLabels();

  for (i = 0; i < labels.length; i++) {
    var label = labels[i];

    if (label.getName() == labelName) {
      return true;
    }
  }

  return false;
}

function markUnresponded(thread) {
  var label = getLabel(unrespondedLabel);
  label.addToThread(thread);
}

function getLabel(labelName) {
  var label = GmailApp.getUserLabelByName(labelName);

  if (label) {
    Logger.log('Label exists.');
  } else {
    Logger.log('Label does not exist. Creating it.');
    label = GmailApp.createLabel(labelName);
  }

  return label;
}

function cleanUp() {
  var label = getLabel(unrespondedLabel),
      iLabel = getLabel(ignoreLabel),
      threads = label.getThreads(),
      numExpired = 0,
      twoWeeksAgo = new Date();

  twoWeeksAgo.setDate(twoWeeksAgo.getDate() - maxDays);

  if (!threads.length) {
    Logger.log('No threads with that label');
    return;
  } else {
    Logger.log('Processing ' + threads.length + ' threads.');
  }

  for (i = 0; i < threads.length; i++) {
    var thread = threads[i],
        lastMessageDate = thread.getLastMessageDate();

    // Remove all labels from expired threads.
    if (lastMessageDate.getTime() < twoWeeksAgo.getTime()) {
      numExpired++;
      Logger.log('Thread expired');
      label.removeFromThread(thread);
      iLabel.removeFromThread(thread);
    } else {
      Logger.log('Thread not expired');
    }
  }
  Logger.log(numExpired + ' unresponded messages expired.');
}
<commit_msg>Stylistic clean up.<commit_after>/*
 * This script goes through your Gmail Inbox and finds recent emails where you
 * were the last respondent. It applies a nice label to them, so you can
 * see them in Priority Inbox or do something else.
 *
 * To remove and ignore an email thread, just remove the unrespondedLabel and
 * apply the ignoreLabel.
 *
 * This is most effective when paired with a time-based script trigger.
 *
 * For installation instructions, read this blog post:
 * http://jonathan-kim.com/2013/Gmail-No-Response/
 */

// Edit these to your liking.
var unrespondedLabel = 'No Response',
    ignoreLabel = 'Ignore No Response',
    minDays = 5,
    maxDays = 14;

function main() {
  processUnresponded();
  cleanUp();
}

function processUnresponded() {
  var threads = GmailApp.search('is:sent from:me -in:chats older_than:' + minDays + 'd newer_than:' + maxDays + 'd'),
      numUpdated = 0,
      minDaysAgo = new Date();

  minDaysAgo.setDate(minDaysAgo.getDate() - minDays);

  // Filter threads where I was the last respondent.
  for (var i = 0; i < threads.length; i++) {
    var thread = threads[i],
        messages = thread.getMessages(),
        lastMessage = messages[messages.length - 1],
        lastFrom = lastMessage.getFrom(),
        lastMessageIsOld = lastMessage.getDate().getTime() < minDaysAgo.getTime();

    if (isFromMe(lastFrom) && lastMessageIsOld && !threadHasLabel(thread, ignoreLabel)) {
      markUnresponded(thread);
      numUpdated++;
    }
  }

  Logger.log('Updated ' + numUpdated + ' messages.');
}

function isFromMe(fromAddress) {
  var addresses = getEmailAddresses();
  for (i = 0; i < addresses.length; i++) {
    var address = addresses[i],
        r = RegExp(address);

    if (r.test(fromAddress)) {
      return true;
    }
  }

  return false;
}

function getEmailAddresses() {
  var me = Session.getActiveUser().getEmail(),
      emails = GmailApp.getAliases();

  emails.push(me);
  return emails;
}

function threadHasLabel(thread, labelName) {
  var labels = thread.getLabels();

  for (i = 0; i < labels.length; i++) {
    var label = labels[i];

    if (label.getName() == labelName) {
      return true;
    }
  }

  return false;
}

function markUnresponded(thread) {
  var label = getLabel(unrespondedLabel);
  label.addToThread(thread);
}

function getLabel(labelName) {
  var label = GmailApp.getUserLabelByName(labelName);

  if (label) {
    Logger.log('Label exists.');
  } else {
    Logger.log('Label does not exist. Creating it.');
    label = GmailApp.createLabel(labelName);
  }

  return label;
}

function cleanUp() {
  var label = getLabel(unrespondedLabel),
      iLabel = getLabel(ignoreLabel),
      threads = label.getThreads(),
      numExpired = 0,
      twoWeeksAgo = new Date();

  twoWeeksAgo.setDate(twoWeeksAgo.getDate() - maxDays);

  if (!threads.length) {
    Logger.log('No threads with that label');
    return;
  } else {
    Logger.log('Processing ' + threads.length + ' threads.');
  }

  for (i = 0; i < threads.length; i++) {
    var thread = threads[i],
        lastMessageDate = thread.getLastMessageDate();

    // Remove all labels from expired threads.
    if (lastMessageDate.getTime() < twoWeeksAgo.getTime()) {
      numExpired++;
      Logger.log('Thread expired');
      label.removeFromThread(thread);
      iLabel.removeFromThread(thread);
    } else {
      Logger.log('Thread not expired');
    }
  }
  Logger.log(numExpired + ' unresponded messages expired.');
}
<|endoftext|>


<commit_before>var runner = require('run-qunit')

var createResultsHandler = function(ctx){
  return function(res){
    console.log("strider-qunit > Results:", res);
    ctx.events.emit('testDone', res);
  }
}

module.exports = function(ctx, cb) {

    ctx.addDetectionRule({
            filename: "test/index.html"
          , grep: /qunit/gi
          , exists: true
          , language: "javascript"
          , framework: "qunit"
          , prepare: function(ctx, cb){

              if (!ctx.events){
                throw "strider-qunit requires a worker with events bus - is your strider-simple-worker out of date?"
              }

              var cgi = require('gateway')(ctx.workingDir, {'.php': 'php-cgi'})

              var cgiwrap = function(){
                console.log("!!! -> CGI: ", req.url)
                return cgi.apply(this, arguments);
              }

              var opts = {
                // TODO use path() for :
                testfile : ctx.workingDir + '/test/index.html' // TODO override from db
              , testdir: ctx.workingDir  // TODO overide from DB
              , port: 4000
              , path: ctx.workingDir
              , useID : true
              , middleware : [cgi]
              , progressCB: function(err, data){
                  ctx.striderMessage("QUnit Progress (Job "+
                      data.id + ") " + data.tests_run + " tests run");
                  if (data.tracebacks){
                    for (var i = 0; i<data.tracebacks.length; i++){
                      ctx.striderMessage("\n\n[ERROR]" + data.tracebacks[i]);
                    }
                  }
                  console.log("QUnit Progress", data);
                }
              }

             ctx.striderMessage("Setting up qunit server");
             ctx.browsertestPort =  opts.port;
             ctx.browsertestPath = "/test/index.html" //TODO overwrite from DB

             runner.start(opts, createResultsHandler(ctx), function(){
               ctx.striderMessage("Strider-QUnit Runner Started");
               cb(0)
             });

           }

           , cleanup: function(ctx, cb){
            if (runner.open()){
              runner.close()
            }
            cb(0)
          }
        })

  console.log("strider-qunit extension loaded")
  cb(null, null);
}
<commit_msg>last tracebacks printed<commit_after>var runner = require('run-qunit')

var createResultsHandler = function(ctx){
  return function(res){
    if (res.tracebacks){
      for (var i = 0; i<res.tracebacks.length; i++){
        ctx.striderMessage("\n\n[ERROR]" + res.tracebacks[i]);
      }
    }
    console.log("strider-qunit > Results:", res);
    ctx.events.emit('testDone', res);
  }
}

module.exports = function(ctx, cb) {

    ctx.addDetectionRule({
            filename: "test/index.html"
          , grep: /qunit/gi
          , exists: true
          , language: "javascript"
          , framework: "qunit"
          , prepare: function(ctx, cb){

              if (!ctx.events){
                throw "strider-qunit requires a worker with events bus - is your strider-simple-worker out of date?"
              }

              var cgi = require('gateway')(ctx.workingDir, {'.php': 'php-cgi'})

              var cgiwrap = function(){
                console.log("!!! -> CGI: ", req.url)
                return cgi.apply(this, arguments);
              }

              var opts = {
                // TODO use path() for :
                testfile : ctx.workingDir + '/test/index.html' // TODO override from db
              , testdir: ctx.workingDir  // TODO overide from DB
              , port: 4000
              , path: ctx.workingDir
              , useID : true
              , middleware : [cgi]
              , progressCB: function(err, data){
                  ctx.striderMessage("QUnit Progress (Job "+
                      data.id + ") " + data.tests_run + " tests run");
                  if (data.tracebacks){
                    for (var i = 0; i<data.tracebacks.length; i++){
                      ctx.striderMessage("\n\n[ERROR]" + data.tracebacks[i]);
                    }
                  }
                  console.log("QUnit Progress", data);
                }
              }

             ctx.striderMessage("Setting up qunit server");
             ctx.browsertestPort =  opts.port;
             ctx.browsertestPath = "/test/index.html" //TODO overwrite from DB

             runner.start(opts, createResultsHandler(ctx), function(){
               ctx.striderMessage("Strider-QUnit Runner Started");
               cb(0)
             });

           }

           , cleanup: function(ctx, cb){
            if (runner.open()){
              runner.close()
            }
            cb(0)
          }
        })

  console.log("strider-qunit extension loaded")
  cb(null, null);
}
<|endoftext|>


<commit_before>describe('cache', () => {
    const assert = require('assert')
    const Cache = require('../cache')
    it('can be constructed', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'constructed')
    })
    it('can cache an object', () => {
        const cache = new Cache
        const entry = cache.hold([ 1 ], 1)
        assert.equal(entry.value, 1, 'cached')
        entry.release()
    })
    it('can set heft', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const entry = cache.hold([ 1 ], 1)
        assert.equal(entry.value, 1, 'cached')
        assert.equal(entry.heft, 0, 'initial entry heft')
        entry.heft = 1
        assert.equal(cache.heft, 1, 'updated entry heft')
        assert.equal(cache.heft, 1, 'updated cache heft')
        entry.release()
    })
    it('can get a cached object', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const first = cache.hold([ 1 ], 1)
        assert.equal(first.value, 1, 'cached')
        first.release()
        const second = cache.hold([ 1 ], 2)
        assert.equal(second.value, 1, 'got cached')
        second.release()
    })
    it('can get remove a cached object', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const first = cache.hold([ 1 ], 1)
        assert.equal(first.value, 1, 'cached')
        first.heft = 1
        assert.equal(cache.heft, 1, 'set entry heft')
        first.release()
        const second = cache.hold([ 1 ], 2)
        assert.equal(second.value, 1, 'got cached')
        second.remove()
        assert.equal(cache.heft, 0, 'removed object')
        const third = cache.hold([ 1 ], 2)
        assert.equal(third.value, 2, 'inserted new object')
        third.remove()
    })
    it('can purge objects', () => {
        const cache = new Cache
        const first = cache.hold([ 1 ], 1)
        first.heft = 1
        const second = cache.hold([ 2 ], 1)
        second.heft = 1
        second.release()
        const third = cache.hold([ 3 ], 1)
        third.heft = 1
        third.release()
        assert.equal(cache.heft, 3, 'cache heft at 3')
        cache.purge(2)
        assert.equal(cache.heft, 2, 'cache heft at 2')
    })
})
<commit_msg>Fix cache test assertion.<commit_after>describe('cache', () => {
    const assert = require('assert')
    const Cache = require('../cache')
    it('can be constructed', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'constructed')
    })
    it('can cache an object', () => {
        const cache = new Cache
        const entry = cache.hold([ 1 ], 1)
        assert.equal(entry.value, 1, 'cached')
        entry.release()
    })
    it('can set heft', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const entry = cache.hold([ 1 ], 1)
        assert.equal(entry.value, 1, 'cached')
        assert.equal(entry.heft, 0, 'initial entry heft')
        entry.heft = 1
        assert.equal(entry.heft, 1, 'updated entry heft')
        assert.equal(cache.heft, 1, 'updated cache heft')
        entry.release()
    })
    it('can get a cached object', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const first = cache.hold([ 1 ], 1)
        assert.equal(first.value, 1, 'cached')
        first.release()
        const second = cache.hold([ 1 ], 2)
        assert.equal(second.value, 1, 'got cached')
        second.release()
    })
    it('can get remove a cached object', () => {
        const cache = new Cache
        assert.equal(cache.heft, 0, 'initial cache heft')
        const first = cache.hold([ 1 ], 1)
        assert.equal(first.value, 1, 'cached')
        first.heft = 1
        assert.equal(cache.heft, 1, 'set entry heft')
        first.release()
        const second = cache.hold([ 1 ], 2)
        assert.equal(second.value, 1, 'got cached')
        second.remove()
        assert.equal(cache.heft, 0, 'removed object')
        const third = cache.hold([ 1 ], 2)
        assert.equal(third.value, 2, 'inserted new object')
        third.remove()
    })
    it('can purge objects', () => {
        const cache = new Cache
        const first = cache.hold([ 1 ], 1)
        first.heft = 1
        const second = cache.hold([ 2 ], 1)
        second.heft = 1
        second.release()
        const third = cache.hold([ 3 ], 1)
        third.heft = 1
        third.release()
        assert.equal(cache.heft, 3, 'cache heft at 3')
        cache.purge(2)
        assert.equal(cache.heft, 2, 'cache heft at 2')
    })
})
<|endoftext|>


<commit_before><commit_msg>Update spect.js<commit_after><|endoftext|>


<commit_before>module('test tests', {
	setup:function (){
		console.log('module setup for '+this.__testInst.name);
		var env=this;
		this.__testInst.requireCustomLib('util', function (exports){
			env.testUtils=exports;
		});
	},
	teardown:function (){
		console.log('module teardown for '+this.__testInst.name);
	}
});
test('test ...', 1, function (){
	ok(true, 'test has testUtils');
	if(this.testUtils){
		expect(3);
		ok(this.testUtils.setPath, 'testUtils has setPath');
		ok(this.testUtils.ensurePath, 'test has ensurePath');
		console.dir(this.testUtils)
	}else
		ok(false, 'test has testUtils');
});
asyncTest('async test ...', 1, function (){
	setTimeout(function(){
		ok(true, 'successfull asyncTest');
		start();
	}, 15);
});
<commit_msg>minor test update<commit_after>module('non-UI tests', {
	setup:function (){
		console.log('module setup for '+this.__testInst.name);
		var env=this;
		this.__testInst.requireCustomLib('util', function (exports){
			env.testUtils=exports;
		});
	},
	teardown:function (){
		console.log('module teardown for '+this.__testInst.name);
	}
});
test('test ...', 1, function (){
	ok(true, 'test has testUtils');
	if(this.testUtils){
		expect(3);
		ok(this.testUtils.setPath, 'testUtils has setPath');
		ok(this.testUtils.ensurePath, 'test has ensurePath');
		console.dir(this.testUtils)
	}else
		ok(false, 'test has testUtils');
});
asyncTest('async test ...', 1, function (){
	setTimeout(function(){
		ok(true, 'successful asyncTest');
		start();
	}, 15);
});
<|endoftext|>


<commit_before>
// stomp-server/frame.js -- Definition of the Frame class

function MalformedFrameError(description) {
    Error.call(this, 'Malformed frame: ' + description );
    this.description = description;
}

function Frame( cmd, headers, body ) {
    var self = this;

    // Handle defaults
    if( body == null ) body = '';

    // Initialize members
    this.cmd = cmd;
    this.headers = {};
    this.body = body;
    this.handled = false;

    // Load headers
    if( headers != null )
        for( var key in headers )
            this.headers[key] = headers[key];

    // Set content length, if applicable
    if( this.body != null )
        this.updateContentLength();
}

Frame.prototype.updateContentLength = function () {
    this.headers['content-length'] = this.body.length;
};

Frame.prototype.toBuffer = function() {
    var buffer = '';

    // Add the command
    buffer += this.cmd + '\n';

    // Add the headers
    for( var key in this.headers )
        buffer += key + ":" + this.headers[key] + '\n';
    buffer += '\n';

    // Add the body
    if( this.body != null )
        buffer += this.body;

    // End the frame
    buffer += "\0";

    // Operation Complete!
    return buffer;
};

function fromBuffer(buffer) {
    // Extract command
    var cmd = buffer.peekLine('\n');
    if( cmd == null ) {
        buffer.abortRead();
        return null;
    }

    // Extract headers
    var headers = {};
    var line = '';
    var rex = /^([^:]+):\ *(.*)$/;
    do {
        // Extract a line
        line = buffer.peekLine('\n');
        if( line == null ) {
            buffer.abortRead();
            return null;
        }

        if( line != '' ) {
            // Parse the header line
            var match = rex.exec(line);
            if( match == null )
                throw new MalformedFrameError('Invalid header: ' + line);

            // Save the header data
            headers[match[1]] = match[2];
        }
    } while(line != '');

    // Parse content-length header
    if( headers['content-length'] != null )
        headers['content-length'] = parseInt(headers['content-length']);

    // Extract the body
    var body = '';
    if( headers['content-length'] != null ) {
        body = buffer.peekString(headers['content-length']);
        if( body == null ) {
            buffer.abortRead();
            return null;
        }

        // Panic if terminator is misplaced
        var verify = buffer.peekString(1);
        if( verify == null ) {
            buffer.abbortRead();
            return null;
        }
        if( verify != '\0' ) {
            buffer.commitRead();
            throw new MalformedFrameError('Frame terminator missing');
        }
    } else {
        // Read body until null terminator
        body = buffer.peekLine('\0');
        if( body == null ) {
            buffer.abortRead();
            return null;
        }
    }

    // Operation Complete!
    buffer.commitRead();
    return new Frame(cmd, headers, body);
}

// Export classes
module.exports.Frame = Frame;
module.exports.fromBuffer = fromBuffer;
<commit_msg>Add a note about consuming spaces after header colons<commit_after>
// stomp-server/frame.js -- Definition of the Frame class

function MalformedFrameError(description) {
    Error.call(this, 'Malformed frame: ' + description );
    this.description = description;
}

function Frame( cmd, headers, body ) {
    var self = this;

    // Handle defaults
    if( body == null ) body = '';

    // Initialize members
    this.cmd = cmd;
    this.headers = {};
    this.body = body;
    this.handled = false;

    // Load headers
    if( headers != null )
        for( var key in headers )
            this.headers[key] = headers[key];

    // Set content length, if applicable
    if( this.body != null )
        this.updateContentLength();
}

Frame.prototype.updateContentLength = function () {
    this.headers['content-length'] = this.body.length;
};

Frame.prototype.toBuffer = function() {
    var buffer = '';

    // Add the command
    buffer += this.cmd + '\n';

    // Add the headers
    for( var key in this.headers )
        buffer += key + ":" + this.headers[key] + '\n';
    buffer += '\n';

    // Add the body
    if( this.body != null )
        buffer += this.body;

    // End the frame
    buffer += "\0";

    // Operation Complete!
    return buffer;
};

function fromBuffer(buffer) {
    // Extract command
    var cmd = buffer.peekLine('\n');
    if( cmd == null ) {
        buffer.abortRead();
        return null;
    }

    // Extract headers
    var headers = {};
    var line = '';
    var rex = /^([^:]+):\ *(.*)$/; // NOTE: This consumes spaces after the colon
    do {
        // Extract a line
        line = buffer.peekLine('\n');
        if( line == null ) {
            buffer.abortRead();
            return null;
        }

        if( line != '' ) {
            // Parse the header line
            var match = rex.exec(line);
            if( match == null )
                throw new MalformedFrameError('Invalid header: ' + line);

            // Save the header data
            headers[match[1]] = match[2];
        }
    } while(line != '');

    // Parse content-length header
    if( headers['content-length'] != null )
        headers['content-length'] = parseInt(headers['content-length']);

    // Extract the body
    var body = '';
    if( headers['content-length'] != null ) {
        body = buffer.peekString(headers['content-length']);
        if( body == null ) {
            buffer.abortRead();
            return null;
        }

        // Panic if terminator is misplaced
        var verify = buffer.peekString(1);
        if( verify == null ) {
            buffer.abbortRead();
            return null;
        }
        if( verify != '\0' ) {
            buffer.commitRead();
            throw new MalformedFrameError('Frame terminator missing');
        }
    } else {
        // Read body until null terminator
        body = buffer.peekLine('\0');
        if( body == null ) {
            buffer.abortRead();
            return null;
        }
    }

    // Operation Complete!
    buffer.commitRead();
    return new Frame(cmd, headers, body);
}

// Export classes
module.exports.Frame = Frame;
module.exports.fromBuffer = fromBuffer;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.5.25 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json
// @resource     sprites sox.sprites.svg

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    const spritesDiv = $('<div/>', { html: GM_getResourceText('sprites') });
    $('head').append(spritesDiv);

    GM_addStyle(GM_getResourceText('css'));

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', target => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [target]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', target => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [target]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', target => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [target]);
    });

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<commit_msg>Bump version number<commit_after>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.5.26 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json
// @resource     sprites sox.sprites.svg

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    const spritesDiv = $('<div/>', { html: GM_getResourceText('sprites') });
    $('head').append(spritesDiv);

    GM_addStyle(GM_getResourceText('css'));

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', target => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [target]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', target => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [target]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', target => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [target]);
    });

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<|endoftext|>


<commit_before>var generators = require('yeoman-generator');
var fse = require('fs-extra');

// Vars
var cwd = process.cwd();

module.exports = generators.Base.extend({

  // ## Clone odddrupal

  // Clone https://github.com/oddhill/odddrupal.git.
  // And rename origin to odddrupal.
  clone: function () {
    var self = this;
    var done = this.async();

    // Check if cwd is empty or not.
    var files = fse.readdirSync(cwd);
    if (files.length > 0) {
      // quit
      self.log('Current dir not empty! Emergency, quits.');
      process.exit();
      return;
    }

    self.log('Cloning odddrupal to ' + cwd + '...');

    // Clone odddrupal to cwd
    var clone = self.spawnCommand('git', ['clone', 'https://github.com/oddhill/odddrupal.git', cwd]);
    clone.on('close', function () {
      // Remove origin remote ref
      var rmRemote = self.spawnCommand('git', ['remote', 'rm', 'origin'], {cwd: cwd});
      rmRemote.on('close', function (code) {
        if (!code) {
          self.log('Done');
        }
        else {
          self.log('Unable to remove origin remote reference. Code: ' + code);
        }
        // Continue
        done();
      });
    });
  },

  // Rename current branch to master
  createMaster: function () {
    var self = this;
    var done = this.async();
    self.log('Renaming the 7.x branch to master...');
    // Rename current local branch to master
    var rename = self.spawnCommand('git', ['branch', '-m', '7.x', 'master']);
    rename.on('close', function () {
      self.log('Done');
      done();
    });
  },

  // ## Install odddrupal

  // Copy .htaccess.default to .htaccess.
  htaccess: function () {
    var self = this;
    var done = this.async();
    fse.copy(cwd + '/.htaccess.default', cwd + '/.htaccess', function(err) {
      if (!err) {
        self.log('Copied .htaccess.default to .htaccess');
        done();
      }
    });
  },

  // Copy sites/default/settings.local.php.default to settings.local.php.
  dbSettings: function () {
    var self = this;
    var done = this.async();
    fse.copy(cwd + '/sites/default/settings.local.php.default', cwd + '/sites/default/settings.local.php', function(err) {
      if (!err) {
        self.log('Copied settings.local.php.default to settings.local.php');

        done();
      }
    });
  },

  // Make sure the files folder (sites/all/files) is 777.
  filesChmod: function () {
    var self = this;
    var done = this.async();
    fse.ensureDir(cwd + '/sites/all/files', function(err) {
      if (!err) {
        fse.chmodSync(cwd + '/sites/all/files', 777);
        self.log('Created files dir');

        // Continue
        done();
      }
    });
  },

  end: function () {
    this.log('All done!');

    // Exit the process
    // This needs to be done because of a bug in the nodegit lib.
    // The nodegit process never exits..
    // https://github.com/nodegit/nodegit/issues/497
    process.exit();
  }

});
<commit_msg>Do not kill the process<commit_after>var generators = require('yeoman-generator');
var fse = require('fs-extra');

// Vars
var cwd = process.cwd();

module.exports = generators.Base.extend({

  // ## Clone odddrupal

  // Clone https://github.com/oddhill/odddrupal.git.
  // And rename origin to odddrupal.
  clone: function () {
    var self = this;
    var done = this.async();

    // Check if cwd is empty or not.
    var files = fse.readdirSync(cwd);
    if (files.length > 0) {
      // quit
      self.log('Current dir not empty! Emergency, quits.');
      process.exit();
      return;
    }

    self.log('Cloning odddrupal to ' + cwd + '...');

    // Clone odddrupal to cwd
    var clone = self.spawnCommand('git', ['clone', 'https://github.com/oddhill/odddrupal.git', cwd]);
    clone.on('close', function () {
      // Remove origin remote ref
      var rmRemote = self.spawnCommand('git', ['remote', 'rm', 'origin'], {cwd: cwd});
      rmRemote.on('close', function (code) {
        if (!code) {
          self.log('Done');
        }
        else {
          self.log('Unable to remove origin remote reference. Code: ' + code);
        }
        // Continue
        done();
      });
    });
  },

  // Rename current branch to master
  createMaster: function () {
    var self = this;
    var done = this.async();
    self.log('Renaming the 7.x branch to master...');
    // Rename current local branch to master
    var rename = self.spawnCommand('git', ['branch', '-m', '7.x', 'master']);
    rename.on('close', function () {
      self.log('Done');
      done();
    });
  },

  // ## Install odddrupal

  // Copy .htaccess.default to .htaccess.
  htaccess: function () {
    var self = this;
    var done = this.async();
    fse.copy(cwd + '/.htaccess.default', cwd + '/.htaccess', function(err) {
      if (!err) {
        self.log('Copied .htaccess.default to .htaccess');
        done();
      }
    });
  },

  // Copy sites/default/settings.local.php.default to settings.local.php.
  dbSettings: function () {
    var self = this;
    var done = this.async();
    fse.copy(cwd + '/sites/default/settings.local.php.default', cwd + '/sites/default/settings.local.php', function(err) {
      if (!err) {
        self.log('Copied settings.local.php.default to settings.local.php');

        done();
      }
    });
  },

  // Make sure the files folder (sites/all/files) is 777.
  filesChmod: function () {
    var self = this;
    var done = this.async();
    fse.ensureDir(cwd + '/sites/all/files', function(err) {
      if (!err) {
        fse.chmodSync(cwd + '/sites/all/files', 777);
        self.log('Created files dir');

        // Continue
        done();
      }
    });
  },

  end: function () {
    this.log('All done!');
  }

});
<|endoftext|>


<commit_before>(function(window) {
	var HAS_HASHCHANGE = (function() {
		var doc_mode = window.documentMode;
		return ('onhashchange' in window) &&
			(doc_mode === undefined || doc_mode > 7);
	})();

	L.Hash = function(map) {
		this.onHashChange = L.Util.bind(this.onHashChange, this);

		if (map) {
			this.init(map);
		}
	};

	L.Hash.parseHash = function(hash) {
		if(hash.indexOf('#') === 0) {
			hash = hash.substr(1);
		}
		var args = hash.split("/");
		if (args.length == 3) {
			var zoom = parseInt(args[0], 10),
			lat = parseFloat(args[1]),
			lon = parseFloat(args[2]);
			if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
				return false;
			} else {
				return {
					center: new L.LatLng(lat, lon),
					zoom: zoom
				};
			}
		} else {
			return false;
		}
	};

	L.Hash.prototype = {
		map: null,
		lastHash: null,

		parseHash: L.Hash.parseHash,

		formatHash: function(map) {
			var center = map.getCenter(),
			    zoom = map.getZoom(),
			    precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));

			return "#" + [zoom,
				center.lat.toFixed(precision),
				center.lng.toFixed(precision)
			].join("/");
		},

		init: function(map) {
			this.map = map;

			// reset the hash
			this.lastHash = null;
			this.onHashChange();

			if (!this.isListening) {
				this.startListening();
			}
		},

		remove: function() {
			if (this.changeTimeout) {
				clearTimeout(this.changeTimeout);
			}

			if (this.isListening) {
				this.stopListening();
			}

			this.map = null;
		},

		onMapMove: function() {
			// bail if we're moving the map (updating from a hash),
			// or if the map is not yet loaded

			if (this.movingMap || !this.map._loaded) {
				return false;
			}

			var hash = this.formatHash(this.map);
			if (this.lastHash != hash) {
				location.replace(hash);
				this.lastHash = hash;
			}
		},

		movingMap: false,
		update: function() {
			var hash = location.hash;
			if (hash === this.lastHash) {
				return;
			}
			var parsed = this.parseHash(hash);
			if (parsed) {
				this.movingMap = true;

				this.map.setView(parsed.center, parsed.zoom);

				this.movingMap = false;
			} else {
				this.onMapMove(this.map);
			}
		},

		// defer hash change updates every 100ms
		changeDefer: 100,
		changeTimeout: null,
		onHashChange: function() {
			// throttle calls to update() so that they only happen every
			// `changeDefer` ms
			if (!this.changeTimeout) {
				var that = this;
				this.changeTimeout = setTimeout(function() {
					that.update();
					that.changeTimeout = null;
				}, this.changeDefer);
			}
		},

		isListening: false,
		hashChangeInterval: null,
		startListening: function() {
			this.map.on("moveend", this.onMapMove, this);

			if (HAS_HASHCHANGE) {
				L.DomEvent.addListener(window, "hashchange", this.onHashChange);
			} else {
				clearInterval(this.hashChangeInterval);
				this.hashChangeInterval = setInterval(this.onHashChange, 50);
			}
			this.isListening = true;
		},

		stopListening: function() {
			this.map.off("moveend", this.onMapMove, this);

			if (HAS_HASHCHANGE) {
				L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
			} else {
				clearInterval(this.hashChangeInterval);
			}
			this.isListening = false;
		}
	};
	L.hash = function(map) {
		return new L.Hash(map);
	};
	L.Map.prototype.addHash = function() {
		this._hash = L.hash(this);
	};
	L.Map.prototype.removeHash = function() {
		this._hash.remove();
	};
})(window);
<commit_msg>Expose formatHash too<commit_after>(function(window) {
	var HAS_HASHCHANGE = (function() {
		var doc_mode = window.documentMode;
		return ('onhashchange' in window) &&
			(doc_mode === undefined || doc_mode > 7);
	})();

	L.Hash = function(map) {
		this.onHashChange = L.Util.bind(this.onHashChange, this);

		if (map) {
			this.init(map);
		}
	};

	L.Hash.parseHash = function(hash) {
		if(hash.indexOf('#') === 0) {
			hash = hash.substr(1);
		}
		var args = hash.split("/");
		if (args.length == 3) {
			var zoom = parseInt(args[0], 10),
			lat = parseFloat(args[1]),
			lon = parseFloat(args[2]);
			if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
				return false;
			} else {
				return {
					center: new L.LatLng(lat, lon),
					zoom: zoom
				};
			}
		} else {
			return false;
		}
	};

	L.Hash.formatHash = function(map) {
		var center = map.getCenter(),
		    zoom = map.getZoom(),
		    precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));

		return "#" + [zoom,
			center.lat.toFixed(precision),
			center.lng.toFixed(precision)
		].join("/");
	},

	L.Hash.prototype = {
		map: null,
		lastHash: null,

		parseHash: L.Hash.parseHash,
		formatHash: L.Hash.formatHash,

		init: function(map) {
			this.map = map;

			// reset the hash
			this.lastHash = null;
			this.onHashChange();

			if (!this.isListening) {
				this.startListening();
			}
		},

		remove: function() {
			if (this.changeTimeout) {
				clearTimeout(this.changeTimeout);
			}

			if (this.isListening) {
				this.stopListening();
			}

			this.map = null;
		},

		onMapMove: function() {
			// bail if we're moving the map (updating from a hash),
			// or if the map is not yet loaded

			if (this.movingMap || !this.map._loaded) {
				return false;
			}

			var hash = this.formatHash(this.map);
			if (this.lastHash != hash) {
				location.replace(hash);
				this.lastHash = hash;
			}
		},

		movingMap: false,
		update: function() {
			var hash = location.hash;
			if (hash === this.lastHash) {
				return;
			}
			var parsed = this.parseHash(hash);
			if (parsed) {
				this.movingMap = true;

				this.map.setView(parsed.center, parsed.zoom);

				this.movingMap = false;
			} else {
				this.onMapMove(this.map);
			}
		},

		// defer hash change updates every 100ms
		changeDefer: 100,
		changeTimeout: null,
		onHashChange: function() {
			// throttle calls to update() so that they only happen every
			// `changeDefer` ms
			if (!this.changeTimeout) {
				var that = this;
				this.changeTimeout = setTimeout(function() {
					that.update();
					that.changeTimeout = null;
				}, this.changeDefer);
			}
		},

		isListening: false,
		hashChangeInterval: null,
		startListening: function() {
			this.map.on("moveend", this.onMapMove, this);

			if (HAS_HASHCHANGE) {
				L.DomEvent.addListener(window, "hashchange", this.onHashChange);
			} else {
				clearInterval(this.hashChangeInterval);
				this.hashChangeInterval = setInterval(this.onHashChange, 50);
			}
			this.isListening = true;
		},

		stopListening: function() {
			this.map.off("moveend", this.onMapMove, this);

			if (HAS_HASHCHANGE) {
				L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
			} else {
				clearInterval(this.hashChangeInterval);
			}
			this.isListening = false;
		}
	};
	L.hash = function(map) {
		return new L.Hash(map);
	};
	L.Map.prototype.addHash = function() {
		this._hash = L.hash(this);
	};
	L.Map.prototype.removeHash = function() {
		this._hash.remove();
	};
})(window);
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const Promise = require('bluebird');
const os = require('os');
const fs = require('fs');
const path = require('path');
const program = require('commander');
const request = require('request-promise');
const pkg = require('./package.json');

const defaultConfigFile = path.join(os.homedir(),'.godaddy-dns.json');
const defaultLastIpFile = path.join(os.tmpdir(), '.lastip');

program
	.version(pkg.version)
	.option('-c, --config [file]', `specify the configuration file to use (default "${defaultConfigFile}")`)
	.option('-i, --ipfile [file]', `specify which file to use to store the last found ip (default "${defaultLastIpFile}")`)
	.parse(process.argv)
;

const config = JSON.parse(fs.readFileSync(program.config || defaultConfigFile, 'utf8'));
const lastIpFile = program.ipfile || defaultLastIpFile;

function getCurrentIp() {
    return request('https://api.ipify.org/');
}

function getLastIp() {
	return new Promise((resolve, reject) => {
		if (!fs.existsSync(lastIpFile)) {
			return resolve(undefined);
		}

		fs.readFile(lastIpFile, 'utf8', (err, ip) => {
			if (err) {
				return reject(err);
			}

			resolve(ip);
		});
	});
}

function saveLastIp(ip) {
	return new Promise((resolve, reject) => {
		fs.writeFile(lastIpFile, ip, 'utf8', (err) => {
			if (err) {
				return reject(err);
			}

			resolve();
		});
	});
}

function updateRecords(ip) {
	let recordDefaults = {
		type: 'A',
		data: ip,
		ttl: 60 * 10 // 10 minutes (minimum allowed)
	};

	let records = config.records;
	// if records is a single object or string wrap it into an array
	if (records.constructor !== Array) {
		records = [records];
	}
	records = records.map((record) => {
		// if current record is a single string
		if (typeof record === 'string') {
			record = {name: record};
		}
		return Object.assign(recordDefaults, record);
	});

	return Promise.resolve(records)
	    .each((record) => {

            let options = {
                method: 'PUT',
                url: `https://api.godaddy.com/v1/domains/${config.domain}/records/${record.type}/${record.name.replace("@","%40")}`,
                headers: {
                    authorization: `sso-key ${config.apiKey}:${config.secret}`,
                    'content-type': 'application/json'
                },
                body: record,
                json: true
            };

            return request(options);

	    });

}

let lastIp;
let currentIp;

getLastIp()
.then((ip) => {
	lastIp = ip;
	return getCurrentIp();
})
.then((ip) => {
	currentIp = ip;
	if (lastIp === currentIp) {
		return Promise.reject()
	}

	return updateRecords(currentIp);
})
.then(() => {
	return saveLastIp(currentIp);
})
.then(() => {
	console.log(`[${new Date()}] Successfully updated DNS records to ip ${currentIp}`);
})
.catch((err) => {
	if (err) {
		console.error(`[${new Date()}] ${err}`);
		process.exit(1);
	}
});
<commit_msg>fix mutation of recordDefaults (#18)<commit_after>#!/usr/bin/env node

'use strict';

const Promise = require('bluebird');
const os = require('os');
const fs = require('fs');
const path = require('path');
const program = require('commander');
const request = require('request-promise');
const pkg = require('./package.json');

const defaultConfigFile = path.join(os.homedir(),'.godaddy-dns.json');
const defaultLastIpFile = path.join(os.tmpdir(), '.lastip');

program
	.version(pkg.version)
	.option('-c, --config [file]', `specify the configuration file to use (default "${defaultConfigFile}")`)
	.option('-i, --ipfile [file]', `specify which file to use to store the last found ip (default "${defaultLastIpFile}")`)
	.parse(process.argv)
;

const config = JSON.parse(fs.readFileSync(program.config || defaultConfigFile, 'utf8'));
const lastIpFile = program.ipfile || defaultLastIpFile;

function getCurrentIp() {
    return request('https://api.ipify.org/');
}

function getLastIp() {
	return new Promise((resolve, reject) => {
		if (!fs.existsSync(lastIpFile)) {
			return resolve(undefined);
		}

		fs.readFile(lastIpFile, 'utf8', (err, ip) => {
			if (err) {
				return reject(err);
			}

			resolve(ip);
		});
	});
}

function saveLastIp(ip) {
	return new Promise((resolve, reject) => {
		fs.writeFile(lastIpFile, ip, 'utf8', (err) => {
			if (err) {
				return reject(err);
			}

			resolve();
		});
	});
}

function updateRecords(ip) {
	let recordDefaults = {
		type: 'A',
		data: ip,
		ttl: 60 * 10 // 10 minutes (minimum allowed)
	};

	let records = config.records;
	// if records is a single object or string wrap it into an array
	if (records.constructor !== Array) {
		records = [records];
	}
	records = records.map((record) => {
		// if current record is a single string
		if (typeof record === 'string') {
			record = {name: record};
		}
		return Object.assign({}, recordDefaults, record);
	});

	return Promise.resolve(records)
	    .each((record) => {

            let options = {
                method: 'PUT',
                url: `https://api.godaddy.com/v1/domains/${config.domain}/records/${record.type}/${record.name.replace("@","%40")}`,
                headers: {
                    authorization: `sso-key ${config.apiKey}:${config.secret}`,
                    'content-type': 'application/json'
                },
                body: record,
                json: true
            };

            return request(options);

	    });

}

let lastIp;
let currentIp;

getLastIp()
.then((ip) => {
	lastIp = ip;
	return getCurrentIp();
})
.then((ip) => {
	currentIp = ip;
	if (lastIp === currentIp) {
		return Promise.reject()
	}

	return updateRecords(currentIp);
})
.then(() => {
	return saveLastIp(currentIp);
})
.then(() => {
	console.log(`[${new Date()}] Successfully updated DNS records to ip ${currentIp}`);
})
.catch((err) => {
	if (err) {
		console.error(`[${new Date()}] ${err}`);
		process.exit(1);
	}
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.5.5 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    GM_addStyle(GM_getResourceText('css'));
    $('<link/>', {
      rel: 'stylesheet',
      href: 'https://use.fontawesome.com/releases/v5.1.0/css/all.css',
    }).appendTo('head');

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', target => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [target]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', target => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [target]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', target => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [target]);
    });

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<commit_msg>Bump version number<commit_after>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.5.6 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    GM_addStyle(GM_getResourceText('css'));
    $('<link/>', {
      rel: 'stylesheet',
      href: 'https://use.fontawesome.com/releases/v5.1.0/css/all.css',
    }).appendTo('head');

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', target => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [target]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', target => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [target]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', target => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [target]);
    });

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<|endoftext|>


<commit_before>steal.plugins('jquery/event').then(function( $ ) {
	/**
	 * @add jQuery.event.special
	 */
	var resizers = $(),
		resizeCount = 0,
		// bind on the window window resizes to happen
		win = $(window),
		windowWidth = 0,
		windowHeight = 0,
		timer;

	$(function() {
		windowWidth = win.width();
		windowHeight = win.height();
	})

	/**
	 * @attribute resize
	 * @parent specialevents
	 *
	 * The resize event is useful for updating elements dimensions when a parent element
	 * has been resized.  It allows you to only resize elements that need to be resized
	 * in the 'right order'.
	 *
	 * By listening to a resize event, you will be alerted whenever a parent
	 * element has a resize event triggered on it.  For example:
	 *
	 *     $('#foo').bind('resize', function(){
	 *        // adjust #foo's dimensions
	 *     })
	 *
	 *     $(document.body).trigger("resize");
	 *
	 * ## The 'Right Order'
	 *
	 * When a control changes size, typically, you want only internal controls to have to adjust their
	 * dimensions.  Furthermore, you want to adjust controls from the 'outside-in', meaning
	 * that the outermost control adjusts its dimensions before child controls adjust theirs.
	 *
	 * Resize calls resize events in exactly this manner.
	 *
	 * When you trigger a resize event, it will propagate up the DOM until it reaches
	 * an element with the first resize event
	 * handler.  There it will move the event in the opposite direction, calling the element's
	 * children's resize event handlers.
	 *
	 * ## Stopping Children Updates
	 *
	 * If your element doesn't need to change it's dimensions as a result of the parent element, it should
	 * call ev.stopPropagation().  This will only stop resize from being sent to child elements of the current element.
	 *
	 *
	 */
	$.event.special.resize = {
		setup: function( handleObj ) {
			// add and sort the resizers array
			// don't add window because it can't be compared easily
			if ( this !== window ) {
				resizers.push(this);
				$.unique(resizers);
			}
			// returns false if the window
			return this !== window;
		},
		teardown: function() {
			// we shouldn't have to sort
			resizers = resizers.not(this);

			// returns false if the window
			return this !== window;
		},
		add: function( handleObj ) {
			// increment the number of resizer elements
			//$.data(this, "jquery.dom.resizers", ++$.data(this, "jquery.dom.resizers") );
			var origHandler = handleObj.handler;
			handleObj.origHandler = origHandler;

			handleObj.handler = function( ev, data ) {
				var isWindow = this === window;

				// if we are the window and a real resize has happened
				// then we check if the dimensions actually changed
				// if they did, we will wait a brief timeout and
				// trigger resize on the window
				// this is for IE, to prevent window resize 'infinate' loop issues
				if ( isWindow && ev.originalEvent ) {
					var width = win.width(),
						height = win.height();

					if ((width != windowWidth || height != windowHeight)) {
						//update the new dimensions
						windowWidth = width;
						windowHeight = height;
						clearTimeout(timer)
						timer = setTimeout(function() {
							win.trigger("resize");
						}, 1);

					}
					return;
				}

				// if this is the first handler for this event ...
				if ( resizeCount === 0 ) {
					// prevent others from doing what we are about to do
					resizeCount++;

					//trigger all this element's handlers
					$.event.handle.call(this, ev);
					if ( ev.isPropagationStopped() ) {
						resizeCount--;
						return;
					}

					// get all other elements within this element that listen to resize
					// and trigger their resize events
					var index = resizers.index(this),
						length = resizers.length,
						child, sub;

					// if index == -1 it's the window
					while (++index < length && (child = resizers[index]) && (isWindow || $.contains(this, child)) ) {

						// call the event
						$.event.handle.call(child, ev);

						if ( ev.isPropagationStopped() ) {
							// move index until the item is not in the current child
							while (++index < length && (sub = resizers[index]) ) {
								if (!$.contains(child, sub) ) {
									// set index back one
									index--;
									break
								}
							}
						}
					}

					// prevent others from responding
					ev.stopImmediatePropagation();
					resizeCount--;
				} else {
					handleObj.origHandler.call(this, ev, data);
				}
			}
		}
	};

	// automatically bind on these
	$([document, window]).bind('resize', function() {})
})<commit_msg>Modified "add" method to handle the a propagation flag for the resize event. Documented usage for setting the data parameter in the "add" method.<commit_after>steal.plugins('jquery/event').then(function( $ ) {
	/**
	 * @add jQuery.event.special
	 */
	var resizers = $(),
		resizeCount = 0,
		// bind on the window window resizes to happen
		win = $(window),
		windowWidth = 0,
		windowHeight = 0,
		timer;

	$(function() {
		windowWidth = win.width();
		windowHeight = win.height();
	})

	/**
	 * @attribute resize
	 * @parent specialevents
	 *
	 * The resize event is useful for updating elements dimensions when a parent element
	 * has been resized.  It allows you to only resize elements that need to be resized
	 * in the 'right order'.
	 *
	 * By listening to a resize event, you will be alerted whenever a parent
	 * element has a resize event triggered on it.  For example:
	 *
	 *     $('#foo').bind('resize', function(){
	 *        // adjust #foo's dimensions
	 *     })
	 *
	 *     $(document.body).trigger("resize");
	 *
	 * ## The 'Right Order'
	 *
	 * When a control changes size, typically, you want only internal controls to have to adjust their
	 * dimensions.  Furthermore, you want to adjust controls from the 'outside-in', meaning
	 * that the outermost control adjusts its dimensions before child controls adjust theirs.
	 *
	 * Resize calls resize events in exactly this manner.
	 *
	 * When you trigger a resize event, it will propagate up the DOM until it reaches
	 * an element with the first resize event
	 * handler.  There it will move the event in the opposite direction, calling the element's
	 * children's resize event handlers.
	 *
	 * If your intent is to call resize without bubbling and only trigger child element's handlers,
	 * use the following:
	 *
	 *     $("#foo").trigger("resize", false);
	 *
	 * ## Stopping Children Updates
	 *
	 * If your element doesn't need to change it's dimensions as a result of the parent element, it should
	 * call ev.stopPropagation().  This will only stop resize from being sent to child elements of the current element.
	 *
	 *
	 */
	$.event.special.resize = {
		setup: function( handleObj ) {
			// add and sort the resizers array
			// don't add window because it can't be compared easily
			if ( this !== window ) {
				resizers.push(this);
				$.unique(resizers);
			}
			// returns false if the window
			return this !== window;
		},
		teardown: function() {
			// we shouldn't have to sort
			resizers = resizers.not(this);

			// returns false if the window
			return this !== window;
		},
		add: function( handleObj ) {
			// increment the number of resizer elements
			//$.data(this, "jquery.dom.resizers", ++$.data(this, "jquery.dom.resizers") );
			var origHandler = handleObj.handler;
			handleObj.origHandler = origHandler;

			handleObj.handler = function( ev, data ) {
				var isWindow = this === window;

				// if we are the window and a real resize has happened
				// then we check if the dimensions actually changed
				// if they did, we will wait a brief timeout and
				// trigger resize on the window
				// this is for IE, to prevent window resize 'infinate' loop issues
				if ( isWindow && ev.originalEvent ) {
					var width = win.width(),
						height = win.height();

					if ((width != windowWidth || height != windowHeight)) {
						//update the new dimensions
						windowWidth = width;
						windowHeight = height;
						clearTimeout(timer)
						timer = setTimeout(function() {
							win.trigger("resize");
						}, 1);

					}
					return;
				}

				// if this is the first handler for this event ...
				if ( resizeCount === 0 ) {
					// prevent others from doing what we are about to do
					resizeCount++;
					var where = data === false ? ev.target : this
					console.log(where, data);
					//trigger all this element's handlers
					$.event.handle.call(where, ev);
					if ( ev.isPropagationStopped() ) {
						resizeCount--;
						return;
					}

					// get all other elements within this element that listen to resize
					// and trigger their resize events
					var index = resizers.index(this),
						length = resizers.length,
						child, sub;

					// if index == -1 it's the window
					while (++index < length && (child = resizers[index]) && (isWindow || $.contains(where, child)) ) {

						// call the event
						$.event.handle.call(child, ev);

						if ( ev.isPropagationStopped() ) {
							// move index until the item is not in the current child
							while (++index < length && (sub = resizers[index]) ) {
								if (!$.contains(child, sub) ) {
									// set index back one
									index--;
									break
								}
							}
						}
					}

					// prevent others from responding
					ev.stopImmediatePropagation();
					resizeCount--;
				} else {
					handleObj.origHandler.call(this, ev, data);
				}
			}
		}
	};

	// automatically bind on these
	$([document, window]).bind('resize', function() {})
})<|endoftext|>


<commit_before>var spawn = require('child_process').spawn,
    ls    = spawn('explorenfc-basic');

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

ls.on('close', function (code) {
  console.log('child process exited with code ' + code);
});
<commit_msg>reload<commit_after>var spawn = require('child_process').spawn,
    ls    = spawn('explorenfc-basic');

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

ls.on('close', function (code) {
  console.log('child process exited with code ' + code);
  ls = spawn('explorenfc-basic');
});
<|endoftext|>


<commit_before>// Switch these lines once there are useful utils
// const testUtils = require('./utils');
require('./utils');

const path = require('path');
const fs = require('fs-extra');
const {hashElement} = require('folder-hash');

// Mimic how we expect this to be required
const {compress, extract} = require('../');

describe('Compress and Extract should be opposite functions', function () {
    let symlinkPath, folderToSymlink, zipDestination, unzipDestination;

    const cleanUp = () => {
        fs.removeSync(symlinkPath);
        fs.removeSync(zipDestination);
        fs.removeSync(unzipDestination);
    };

    before(function () {
        symlinkPath = path.join(__dirname, 'fixtures', 'theme-symlink');
        folderToSymlink = path.join(__dirname, 'fixtures', 'test-theme');
        zipDestination = path.join(__dirname, 'fixtures', 'theme-symlink.zip');
        unzipDestination = path.join(__dirname, 'fixtures', 'theme-symlink-unzipped');

        cleanUp();
    });

    after(function () {
        cleanUp();
    });

    it('ensure symlinks work', function (done) {
        fs.symlink(folderToSymlink, symlinkPath);

        let originalHash;

        hashElement(symlinkPath)
            .then((_originalHash) => {
                originalHash = _originalHash;
                return compress(symlinkPath, zipDestination);
            })
            .then((res) => {
                res.should.be.an.Object().with.properties('path', 'size');
                res.path.should.eql(zipDestination);
                res.size.should.eql(323805);

                return extract(zipDestination, unzipDestination);
            })
            .then((res) => {
                res.should.be.an.Object().with.properties('path');
                res.path.should.eql(unzipDestination);

                return hashElement(unzipDestination);
            })
            .then((extractedHash) => {
                originalHash.children.toString().should.eql(extractedHash.children.toString());

                done();
            })
            .catch((err) => {
                return done(err);
            });
    });
});
<commit_msg>Updated zip test to reflect intended behaviour<commit_after>// Switch these lines once there are useful utils
// const testUtils = require('./utils');
require('./utils');

const path = require('path');
const fs = require('fs-extra');
const {hashElement} = require('folder-hash');

// Mimic how we expect this to be required
const {compress, extract} = require('../');

describe('Compress and Extract should be opposite functions', function () {
    let symlinkPath, folderToSymlink, zipDestination, unzipDestination;

    const cleanUp = () => {
        fs.removeSync(symlinkPath);
        fs.removeSync(zipDestination);
        fs.removeSync(unzipDestination);
    };

    before(function () {
        symlinkPath = path.join(__dirname, 'fixtures', 'theme-symlink');
        folderToSymlink = path.join(__dirname, 'fixtures', 'test-theme');
        zipDestination = path.join(__dirname, 'fixtures', 'theme-symlink.zip');
        unzipDestination = path.join(__dirname, 'fixtures', 'theme-symlink-unzipped');

        cleanUp();
    });

    after(function () {
        cleanUp();
    });

    it('ensure symlinks work', function (done) {
        fs.symlink(folderToSymlink, symlinkPath);

        let originalHash;

        hashElement(symlinkPath)
            .then((_originalHash) => {
                originalHash = _originalHash;
                return compress(symlinkPath, zipDestination);
            })
            .then((res) => {
                res.should.be.an.Object().with.properties('path', 'size');
                res.path.should.eql(zipDestination);
                res.size.should.be.below(619618);

                return extract(zipDestination, unzipDestination);
            })
            .then((res) => {
                res.should.be.an.Object().with.properties('path');
                res.path.should.eql(unzipDestination);

                return hashElement(unzipDestination);
            })
            .then((extractedHash) => {
                originalHash.children.toString().should.eql(extractedHash.children.toString());

                done();
            })
            .catch((err) => {
                return done(err);
            });
    });
});
<|endoftext|>