<commit_before>var googleImages = require('google-images');
var exif = require('exif2');
var request = require('request');
var fs = require('fs');
var md5 = require('MD5');
var shredfile = require('shredfile')({});
var gui = require('nw.gui');
var exec = require('child_process').exec;
var csv = require('csv-to-json');

// TODO: Check for internet availability.

// Mouse positions; see readMouseMove().
var x;
var y;

function imageSearch(query) {
  var resultsCount = 0;
  var imagesDiv = document.getElementById('images');
  var endOfResults = document.getElementById('eor');
  var eorBreak = document.getElementById('eor-break');

  // The deprecated Google Images API only allows us to recieve a maximum
  // of 60 results.
  for (var i = 0; i < 57; i = i +4) {
    // NOTE: Eventually this should be refactored, but I'm not overly
    // concerned about it at this time.

    /* jshint loopfunc: true */
    googleImages.search(query, { page: i, callback: function(err, images) {
      var resultsDiv = document.getElementById('results');
      if (images[0]) {
      results.className = 'page-header';
      images.forEach(function(image) {
        // NOTE: This is a little hack I implemented to replace imgur
        // thumbnails with the full image.
        if (image.url.substring(0, 19) === 'http://i.imgur.com/') {
          image.url = image.url.replace('b.jpg', '.jpg');
        }

        resultsCount++;
        var file = fs.createWriteStream('./tmp/' + md5(image.url));
        var req = request({url: image.url, proxy: 'http://127.0.0.1:8118'});
        req.pipe(file);

        req.on('end', function() {
          exif ('./tmp/' + md5(image.url), function(err, obj) {
            var exifData = '';
            if (err === null) {
              for(var key in obj) {
                if (obj.hasOwnProperty(key) &&
                    key !== 'exiftool version number' &&
                    key !== 'file name' &&
                    key !== 'directory' &&
                    key !== 'file inode change date time' &&
                    key !== 'file modification date time' &&
                    key !== 'file access date time' &&
                    key !== 'file permissions') {
                    exifData += ucwords(key) + ': ' + obj[key] + '
';
                }
              }
            } else {
              exifData = err;
            }
            shredfile.shred('./tmp/' + md5(image.url), function(err, file) {

            });
            results.innerHTML = '
' + resultsCount + ' Results
';
            // Let's pretend I never wrote this...
            /* jshint maxlen: false */
            imagesDiv.innerHTML += '

';
            eorBreak.className = '';
            endOfResults.className = 'lead text-center text-muted';
          });
        });
      });
      } else {
        eorBreak.className = 'hidden';
        endOfResults.className = 'lead text-center text-muted hidden';
        results.className = 'page-header';
        results.innerHTML = '
No images found.
';
        return false;
      }
    }});
  }
}

/**
 * Uppercase Words
 * @param string str
 */
function ucwords(str) {
    return (str + '').replace(/^([a-z])|\s+([a-z])/g, function($1) {
        return $1.toUpperCase();
    });
}

// Stolen! Credits go to this chap: http://stackoverflow.com/users/1219011/twist
// Original: http://stackoverflow.com/a/11840120
// Modified to remove non-webkit CSS rules.
function getRotationDegrees(obj) {
  var angle;
  var matrix = obj.css('-webkit-transform') ||
  obj.css('transform');
  if (matrix !== 'none') {
    var values = matrix.split('(')[1].split(')')[0].split(',');
    var a = values[0];
    var b = values[1];
    angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
  } else {
    angle = 0;
  }
  return (angle < 0) ? angle +=360 : angle;
}

function showExifData(data) {
  var exifData = document.getElementById('exif-data');
  exifData.innerHTML = window.atob(data);
  $('#exif-data-modal').modal('show');
}

function showContextMenu(url, from) {
  // TODO: Reduce the amount of statements. Yeah, this is a pile of fuck.
  /* jshint maxstatements:25 */
  var menu = new gui.Menu();
  var clipboard = gui.Clipboard.get();

  var flipImageItem = new gui.MenuItem(
    { label: 'Toggle Flip Image',
      click: function() {
        var image = $('img[src="' + url + '"]');
        if (image.hasClass('flipped')) {
          image.removeClass('flipped');
        } else {
          image.addClass('flipped');
        }
      }
    });

  // TODO: Shred images.
  // TODO: Show confidence?
  var genderAgeItem = new gui.MenuItem(
    { label: 'Age/Gender (Experimental)',
      click: function() {
        var file = fs.createWriteStream('./tmp/br/' + md5(url) + '.image');
        var req = request({url: url, proxy: 'http://127.0.0.1:8118'});
        req.pipe(file);

        req.on('end', function() {
          exec('br -algorithm GenderEstimation -enroll ' + process.cwd() +
               '/tmp/br/' + md5(url) + '.image ' + process.cwd() +
               '/tmp/br/gender_' + md5(url) + '.csv', function(err, result) {
            if (err) {
              alert(err);
              return;
            }
            var genderResult = csv.parse('./tmp/br/gender_' + md5(url) +
                                         '.csv');

            var gender = genderResult[0].Gender;
            if (typeof(gender) === 'undefined') {
              gender = 'Unknown';
            }
            var genderSpan = document.getElementById('gender');
            genderSpan.innerHTML = gender;
          });

          exec('br -algorithm AgeEstimation -enroll ' + process.cwd() +
               '/tmp/br/' + md5(url) + '.image ' + process.cwd() +
               '/tmp/br/age_' + md5(url) + '.csv', function(err, result) {
            if (err) {
              alert(err);
              return;
            }
            var ageResult = csv.parse('./tmp/br/age_' + md5(url) + '.csv');

            var age = ageResult[0].Age;
            var ageSpan = document.getElementById('age');
            if (typeof(age) === 'undefined') {
              ageSpan.innerHTML = 'Unknown';
            } else {
              ageSpan.innerHTML = Math.round(age);
            }
          });
          $('#age-gender-modal').modal('show');
        });
      }
    });

  var previewPageItem = new gui.MenuItem(
    { label: 'Preview Page (No Proxy)',
      click: function() {
        // Let's pretend I never wrote this...
        /* jshint maxlen: false */
        var pwin = open('private.html');
        pwin.document.write('');
      }
    });

  var rotateRightItem = new gui.MenuItem(
    { label: 'Rotate Right',
      click: function() {
        var degrees = getRotationDegrees($('img[src="' + url + '"]'));
        $('img[src="' + url + '"]').rotate(degrees + 90);
      }
    });

  var rotateLeftItem = new gui.MenuItem(
    { label: 'Rotate Left',
      click: function() {
        var degrees = getRotationDegrees($('img[src="' + url + '"]'));
        $('img[src="' + url + '"]').rotate(degrees - 90);
      }
    });

  var copyImageUrlItem = new gui.MenuItem(
    { label: 'Copy Image URL',
      click: function() {
        clipboard.set(url, 'text');
      }
    });

  var copyPageUrlItem = new gui.MenuItem(
    { label: 'Copy Page URL',
      click: function() {
        clipboard.set(from, 'text');
      }
    });

  // It's my party and I'll cry if I want to.
  menu.append(previewPageItem);
  menu.append(new gui.MenuItem({ type: 'separator' }));
  menu.append(rotateLeftItem);
  menu.append(rotateRightItem);
  menu.append(flipImageItem);
  menu.append(genderAgeItem);
  menu.append(new gui.MenuItem({ type: 'separator' }));
  menu.append(copyImageUrlItem);
  menu.append(copyPageUrlItem);
  // TODO: Implement these functions.
  menu.append(new gui.MenuItem({ label: 'Save Image' }));
  menu.append(new gui.MenuItem({ label: 'Save Exif Data' }));
  menu.popup(x, y);
}

function readMouseMove(e) {
  x = e.clientX;
  y = e.clientY;
}

$(document).ready(function() {
  document.onmousemove = readMouseMove;

  document.onkeydown = function(e) {
    // Debug console (` key)
    if (e.keyCode === 192) {
      e.preventDefault();
      gui.Window.get().showDevTools();
      return false;
    }
  };

  // We need to reset the age and gender otherwise we're left with
  // stale data.
  $('#age-gender-modal').on('hidden.bs.modal', function () {
      $('#age').html('Waiting...');
      $('#gender').html('Waiting...');
  });

  $('a[href="#settings"]').click(function() {
    $('#settings-modal').modal('show');
    return false;
  });

  $('a[href="#top"]').click(function() {
    $('html, body').animate({ scrollTop: 0 }, 'slow');
    return false;
  });

  $('#eor').click(function() {
    $('html, body').animate({ scrollTop: 100 }, 'slow');
    return false;
  });

  $('#search-form').on('submit', function() {
    $('#images').html('');
    imageSearch($('#query').val());
    return false;
  });

});
<commit_msg>WIP #1 - Check for internet availability.<commit_after>var googleImages = require('google-images');
var exif = require('exif2');
var request = require('request');
var fs = require('fs');
var md5 = require('MD5');
var shredfile = require('shredfile')({});
var gui = require('nw.gui');
var exec = require('child_process').exec;
var csv = require('csv-to-json');

// Mouse positions; see readMouseMove().
var x;
var y;

function imageSearch(query) {
  var resultsCount = 0;
  var imagesDiv = document.getElementById('images');
  var endOfResults = document.getElementById('eor');
  var eorBreak = document.getElementById('eor-break');

  // The deprecated Google Images API only allows us to recieve a maximum
  // of 60 results.
  for (var i = 0; i < 57; i = i +4) {
    // NOTE: Eventually this should be refactored, but I'm not overly
    // concerned about it at this time.

    /* jshint loopfunc: true */
    googleImages.search(query, { page: i, callback: function(err, images) {
      var resultsDiv = document.getElementById('results');
      if (images[0]) {
      results.className = 'page-header';
      images.forEach(function(image) {
        // NOTE: This is a little hack I implemented to replace imgur
        // thumbnails with the full image.
        if (image.url.substring(0, 19) === 'http://i.imgur.com/') {
          image.url = image.url.replace('b.jpg', '.jpg');
        }

        resultsCount++;
        var file = fs.createWriteStream('./tmp/' + md5(image.url));
        var req = request({url: image.url, proxy: 'http://127.0.0.1:8118'});
        req.pipe(file);

        req.on('end', function() {
          exif ('./tmp/' + md5(image.url), function(err, obj) {
            var exifData = '';
            if (err === null) {
              for(var key in obj) {
                if (obj.hasOwnProperty(key) &&
                    key !== 'exiftool version number' &&
                    key !== 'file name' &&
                    key !== 'directory' &&
                    key !== 'file inode change date time' &&
                    key !== 'file modification date time' &&
                    key !== 'file access date time' &&
                    key !== 'file permissions') {
                    exifData += ucwords(key) + ': ' + obj[key] + '
';
                }
              }
            } else {
              exifData = err;
            }
            shredfile.shred('./tmp/' + md5(image.url), function(err, file) {

            });
            results.innerHTML = '
' + resultsCount + ' Results
';
            // Let's pretend I never wrote this...
            /* jshint maxlen: false */
            imagesDiv.innerHTML += '

';
            eorBreak.className = '';
            endOfResults.className = 'lead text-center text-muted';
          });
        });
      });
      } else {
        eorBreak.className = 'hidden';
        endOfResults.className = 'lead text-center text-muted hidden';
        results.className = 'page-header';
        results.innerHTML = '
No images found.
';
        return false;
      }
    }});
  }
}

/**
 * Uppercase Words
 * @param string str
 */
function ucwords(str) {
    return (str + '').replace(/^([a-z])|\s+([a-z])/g, function($1) {
        return $1.toUpperCase();
    });
}

// Stolen! Credits go to this chap: http://stackoverflow.com/users/1219011/twist
// Original: http://stackoverflow.com/a/11840120
// Modified to remove non-webkit CSS rules.
function getRotationDegrees(obj) {
  var angle;
  var matrix = obj.css('-webkit-transform') ||
  obj.css('transform');
  if (matrix !== 'none') {
    var values = matrix.split('(')[1].split(')')[0].split(',');
    var a = values[0];
    var b = values[1];
    angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
  } else {
    angle = 0;
  }
  return (angle < 0) ? angle +=360 : angle;
}

function showExifData(data) {
  var exifData = document.getElementById('exif-data');
  exifData.innerHTML = window.atob(data);
  $('#exif-data-modal').modal('show');
}

function showContextMenu(url, from) {
  // TODO: Reduce the amount of statements. Yeah, this is a pile of fuck.
  /* jshint maxstatements:25 */
  var menu = new gui.Menu();
  var clipboard = gui.Clipboard.get();

  var flipImageItem = new gui.MenuItem(
    { label: 'Toggle Flip Image',
      click: function() {
        var image = $('img[src="' + url + '"]');
        if (image.hasClass('flipped')) {
          image.removeClass('flipped');
        } else {
          image.addClass('flipped');
        }
      }
    });

  // TODO: Shred images.
  // TODO: Show confidence?
  var genderAgeItem = new gui.MenuItem(
    { label: 'Age/Gender (Experimental)',
      click: function() {
        var file = fs.createWriteStream('./tmp/br/' + md5(url) + '.image');
        var req = request({url: url, proxy: 'http://127.0.0.1:8118'});
        req.pipe(file);

        req.on('end', function() {
          exec('br -algorithm GenderEstimation -enroll ' + process.cwd() +
               '/tmp/br/' + md5(url) + '.image ' + process.cwd() +
               '/tmp/br/gender_' + md5(url) + '.csv', function(err, result) {
            if (err) {
              alert(err);
              return;
            }
            var genderResult = csv.parse('./tmp/br/gender_' + md5(url) +
                                         '.csv');

            var gender = genderResult[0].Gender;
            if (typeof(gender) === 'undefined') {
              gender = 'Unknown';
            }
            var genderSpan = document.getElementById('gender');
            genderSpan.innerHTML = gender;
          });

          exec('br -algorithm AgeEstimation -enroll ' + process.cwd() +
               '/tmp/br/' + md5(url) + '.image ' + process.cwd() +
               '/tmp/br/age_' + md5(url) + '.csv', function(err, result) {
            if (err) {
              alert(err);
              return;
            }
            var ageResult = csv.parse('./tmp/br/age_' + md5(url) + '.csv');

            var age = ageResult[0].Age;
            var ageSpan = document.getElementById('age');
            if (typeof(age) === 'undefined') {
              ageSpan.innerHTML = 'Unknown';
            } else {
              ageSpan.innerHTML = Math.round(age);
            }
          });
          $('#age-gender-modal').modal('show');
        });
      }
    });

  var previewPageItem = new gui.MenuItem(
    { label: 'Preview Page (No Proxy)',
      click: function() {
        // Let's pretend I never wrote this...
        /* jshint maxlen: false */
        var pwin = open('private.html');
        pwin.document.write('');
      }
    });

  var rotateRightItem = new gui.MenuItem(
    { label: 'Rotate Right',
      click: function() {
        var degrees = getRotationDegrees($('img[src="' + url + '"]'));
        $('img[src="' + url + '"]').rotate(degrees + 90);
      }
    });

  var rotateLeftItem = new gui.MenuItem(
    { label: 'Rotate Left',
      click: function() {
        var degrees = getRotationDegrees($('img[src="' + url + '"]'));
        $('img[src="' + url + '"]').rotate(degrees - 90);
      }
    });

  var copyImageUrlItem = new gui.MenuItem(
    { label: 'Copy Image URL',
      click: function() {
        clipboard.set(url, 'text');
      }
    });

  var copyPageUrlItem = new gui.MenuItem(
    { label: 'Copy Page URL',
      click: function() {
        clipboard.set(from, 'text');
      }
    });

  // It's my party and I'll cry if I want to.
  menu.append(previewPageItem);
  menu.append(new gui.MenuItem({ type: 'separator' }));
  menu.append(rotateLeftItem);
  menu.append(rotateRightItem);
  menu.append(flipImageItem);
  menu.append(genderAgeItem);
  menu.append(new gui.MenuItem({ type: 'separator' }));
  menu.append(copyImageUrlItem);
  menu.append(copyPageUrlItem);
  // TODO: Implement these functions.
  menu.append(new gui.MenuItem({ label: 'Save Image' }));
  menu.append(new gui.MenuItem({ label: 'Save Exif Data' }));
  menu.popup(x, y);
}

function readMouseMove(e) {
  x = e.clientX;
  y = e.clientY;
}

$(document).ready(function() {
  document.onmousemove = readMouseMove;

  document.onkeydown = function(e) {
    // Debug console (` key)
    if (e.keyCode === 192) {
      e.preventDefault();
      gui.Window.get().showDevTools();
      return false;
    }
  };

  // We need to reset the age and gender otherwise we're left with
  // stale data.
  $('#age-gender-modal').on('hidden.bs.modal', function () {
      $('#age').html('Waiting...');
      $('#gender').html('Waiting...');
  });

  $('a[href="#settings"]').click(function() {
    $('#settings-modal').modal('show');
    return false;
  });

  $('a[href="#top"]').click(function() {
    $('html, body').animate({ scrollTop: 0 }, 'slow');
    return false;
  });

  $('#eor').click(function() {
    $('html, body').animate({ scrollTop: 100 }, 'slow');
    return false;
  });

  $('#search-form').on('submit', function() {
    $('#images').html('');
    imageSearch($('#query').val());
    return false;
  });

});
<|endoftext|>


<commit_before>/**
 * @fileoverview Description of this file.
  To deploy this cloud function to mlab-sandbox, try:

  // Create the buckets
  gsutil mb -p mlab-oti archive-mlab-oti
  gsutil mb -p mlab-oti scraper-mlab-oti
  gsutil mb -p mlab-oti functions-mlab-oti
  // Deploy the functions.
  gcloud beta functions deploy transferOnFileNotification \
    --stage-bucket=functions-mlab-oti \
    --trigger-bucket=scraper-mlab-oti \
    --project=mlab-oti

    CURRENTLY BROKEN
    Things to try?
    GOOGLE_APPLICATION_CREDENTIALS ?

 */

var google = require('googleapis');

exports.fileIsProcessable = function (file) {
  // TODO: make this better. I am sure this is not the only reason to not
  // process a file.
    return (file.resourceState !== 'not_exists');
};

/**
 * Executes a function with default auth.
 *
 * @param {function} func The function to invoke with auth.
 * @param {function} fail The function to call to indicate failure.
 */
exports.executeWithAuth = function (func, fail) {
    google.auth.getApplicationDefault(
        function (err, authClient, projectId) {
            console.log('inside auth, projectId = ', projectId)
            if (err) {
                fail();
                return;
            }

            if (authClient.createScopedRequired && authClient.createScopedRequired()) {
                // This isn't actually executing.
                console.log('createScopedRequired')
                authClient = authClient.createScoped(
                    ['https://www.googleapis.com/auth/cloud-platform']);
            }
            console.log(authClient)

            console.log('executing func')
            func(authClient, projectId);
        }
    );
};

/**
 * Executes a lambda with auth.
 *
 * @param {file} file The object to move.
 * @param {function} done The callback function to indicate cloud function is
 *                        complete.
 */
exports.makeMoveWithAuth = function (file, done) {
    return function (authClient, projectId) {

        console.log('google.storage')
        // Doesn't seem to matter whether we do this, or pass the
        // authClient/project in the storage.objects.copy.
        var storage = google.storage(
            {"version": "v1", "auth": authClient, "project": projectId});
        //var storage = google.storage('v1')

        console.log(storage)
        console.log(file.name)

        // This is consistently failing.
        // The same function, invoked through the cloud API explorer,
        // seems to work just fine, with either cloud-platform, or
        // devstorage.read-write scope.
        console.log('storage.objects.copy')
        storage.objects.copy({
          //  "auth": authClient,
          //  "project": projectId,
            "sourceBucket": file.bucket,
            "sourceObject": file.name,
            //"destinationBucket": "foobar-mlab-oti",
            "destinationBucket": "destination-mlab-sandbox",
            "destinationObject": file.name
        },
      /*  storage.objects.list({
            "auth": authClient,
            "project": projectId,
            "bucket": "scraper-mlab-oti",
            "prefix": "test/"
        }, */

        function(err, msg, incoming) {
            console.log(err)
            console.log(msg)
            console.log(incoming)
            console.log('calling done')
            done(err, msg, incoming)
        });
  }
}

var puts = function(error, stdout, stderr) { sys.puts(stdout); };

/**
 * Determines whether a file object should be embargoed, or transfered
 * immediately.
 *
 * @param {object} file The file under consideration
 */
exports.shouldEmbargo = function (file) {
    return false;
};

/**
 * Cloud Function to be triggered by Cloud Storage,
 * moves the file to the archive-mlab-oti bucket.
 *
 * @param {object} event The Cloud Functions event.
 * @param {function} done The callback function to indicate function complete.
 */
exports.transferOnFileNotification = function transferOnFileNotification (event, done) {
    const file = event.data;

    if (exports.fileIsProcessable(file)) {
        if (exports.shouldEmbargo()) {
            // TODO - notify the embargo system.
        } else {
            exports.executeWithAuth(exports.makeMoveWithAuth(file, done));
        }
    } else {
      done();
    }
};
<commit_msg>This works ONLY with files without a path prefix<commit_after>/**
 * @fileoverview Description of this file.
  To deploy this cloud function to mlab-sandbox, try:

  // Create the buckets
  gsutil mb -p mlab-oti archive-mlab-oti
  gsutil mb -p mlab-oti scraper-mlab-oti
  gsutil mb -p mlab-oti functions-mlab-oti
  // Deploy the functions.
  gcloud beta functions deploy transferOnFileNotification \
    --stage-bucket=functions-mlab-oti \
    --trigger-bucket=scraper-mlab-oti \
    --project=mlab-oti

    CURRENTLY BROKEN
    Things to try?
    GOOGLE_APPLICATION_CREDENTIALS ?

 */

var google = require('googleapis');

exports.fileIsProcessable = function (file) {
  // TODO: make this better. I am sure this is not the only reason to not
  // process a file.
    return (file.resourceState !== 'not_exists');
};

/**
 * Executes a function with default auth.
 *
 * @param {function} func The function to invoke with auth.
 * @param {function} fail The function to call to indicate failure.
 */
exports.executeWithAuth = function (func, fail) {
    google.auth.getApplicationDefault(
        function (err, authClient, projectId) {
            console.log('inside auth, projectId = ', projectId);
            if (err) {
                fail();
                return;
            }

            if (authClient.createScopedRequired && authClient.createScopedRequired()) {
                // This isn't actually executing.
                console.log('createScopedRequired');
                authClient = authClient.createScoped(
                    ['https://www.googleapis.com/auth/cloud-platform']);
            }
            console.log(authClient);

            console.log('executing func');
            func(authClient, projectId);
        }
    );
};

/**
 * Executes a lambda with auth.
 *
 * @param {file} file The object to move.
 * @param {function} done The callback function to indicate cloud function is
 *                        complete.
 */
exports.makeMoveWithAuth = function (file, done) {
    return function (authClient, projectId) {
        var destBucket;  // The destination bucket, based on projectId.
        if (projectId === 'mlab-oti') {
            destBucket = 'archive-mlab-oti';
        } else {
            destBucket = 'destination-mlab-sandbox';
        }

        var storage = google.storage(
            {"version": "v1", "auth": authClient, "project": projectId});

        console.log('copying: ', destBucket, encodeURIComponent(file.name));
        storage.objects.copy({
            "sourceBucket": file.bucket,
            "sourceObject": encodeURIComponent(file.name),
            "destinationBucket": destBucket,
            "destinationObject": encodeURIComponent(file.name)
        },
        // This will be called when copy completes.
        function(err, msg, incoming) {
            if (err) {
                console.log('err: ', err);
                console.log('msg: ', msg);
                console.log('calling done after copy failed.');
                done(err, msg, incoming);
            } else {
                // Delete the object, checking generation in case it changed.
                console.log('deleting file')
                storage.objects.delete({
                    "bucket": file.bucket,
                    "object": encodeURIComponent(file.name),
                    "generation": file.generation,
                },
                // This will be called when delete completes.
                function(err, msg, incoming) {
                    if (err) {
                        console.log(err);
                        console.log('calling done after delete failed.');
                        done(err, msg, incoming);
                    } else {
                        console.log('delete succeeded');
                        done(err, msg, incoming);
                    }
                });
            }
        });
  }
}

var puts = function(error, stdout, stderr) { sys.puts(stdout); };

/**
 * Determines whether a file object should be embargoed, or transfered
 * immediately.
 *
 * @param {object} file The file under consideration
 */
exports.shouldEmbargo = function (file) {
    return false;
};

/**
 * Cloud Function to be triggered by Cloud Storage,
 * moves the file to the archive-mlab-oti bucket.
 *
 * @param {object} event The Cloud Functions event.
 * @param {function} done The callback function to indicate function complete.
 */
exports.transferOnFileNotification = function transferOnFileNotification (event, done) {
    const file = event.data;

    if (exports.fileIsProcessable(file)) {
        if (exports.shouldEmbargo()) {
            // TODO - notify the embargo system.
        } else {
            exports.executeWithAuth(exports.makeMoveWithAuth(file, done));
        }
    } else {
      done();
    }
};
<|endoftext|>


<commit_before>/* global Y, MutationObserver */
'use strict'

function extend (Y) {
  Y.requestModules(['Array', 'Map']).then(function () {
    class YXml extends Y.Array.typeDefinition['class'] {
      constructor (os, _model, _content, attributes, tagname, init) {
        super(os, _model, _content)
        this.attributes = attributes
        this.dom = null
        this._domObserver = null
        this._eventListenerHandler = new Y.utils.EventListenerHandler()
        this.tagname = tagname
        if (init != null && init.dom != null) {
          this._setDom(init.dom)
        }
        super.observe(events => {
          var userEvents = []
          events.forEach(event => {
            if (event.type === 'insert') {
              userEvents.push({
                type: 'childInserted',
                index: event.index,
                node: event.value,
                valueId: event.valueId
              })
            } else if (event.type === 'delete') {
              userEvents.push({
                type: 'childRemoved',
                index: event.index,
                _content: event._content,
                value: event.value
              })
            }
          })
          if (userEvents.length > 0) {
            this._eventListenerHandler.callEventListeners(userEvents)
          }
        })
        attributes.observe(events => {
          var userEvents = []
          events.forEach(event => {
            if (event.type === 'update' || event.type === 'add') {
              userEvents.push({
                type: 'attributeChanged',
                name: event.name,
                value: event.value
              })
            } else if (event.type === 'delete') {
              userEvents.push({
                type: 'attributeRemoved',
                name: event.name
              })
            }
          })
          if (userEvents.length > 0) {
            this._eventListenerHandler.callEventListeners(userEvents)
          }
        })
      }
      _destroy () {
        if (this._domObserver != null) {
          this._domObserver.disconnect()
        }
        this._eventListenerHandler.destroy()
        this._eventListenerHandler = null
        super._destroy()
      }
      insert (pos, types) {
        if (!Array.isArray(types)) {
          throw new Error('Expected an Array of content!')
        }
        types.forEach(function (v) {
          var t = Y.utils.isTypeDefinition(v)
          if (!(v && (
                       typeof v === 'string' ||
                       (t && t[0].class === YXml)
             ))) {
            throw new Error('Expected Y.Xml type or String!')
          }
        })
        super.insert(pos, types)
      }
      // binds to a dom element
      // Only call if dom and YXml are isomorph
      _bindToDom (dom) {
        return new Promise(resolve => {
          // this function makes sure that either the
          // dom event is executed, or the yjs observer is executed
          var token = true
          var mutualExclude = f => {
            // take and process current records
            var records = this._domObserver.takeRecords()
            if (records.length > 0) {
              this._domObserverListener(records)
            }
            if (token) {
              token = false
              try {
                f()
              } catch (e) {
                // discard created records
                this._domObserver.takeRecords()
                token = true
                throw e
              }
              this._domObserver.takeRecords()
              token = true
            }
          }
          this._mutualExclude = mutualExclude
          this._domObserverListener = mutations => {
            mutualExclude(() => {
              mutations.forEach(mutation => {
                if (mutation.type === 'attributes') {
                  this.attributes.set(mutation.attributeName, mutation.target.getAttribute(mutation.attributeName))
                } else if (mutation.type === 'childList') {
                  Array.prototype.forEach.call(Array.prototype.reverse.call(mutation.addedNodes), n => {
                    // compute position
                    var pos
                    if (n.nextSibling == null) {
                      pos = this._content.length
                    } else {
                      pos = this._content.findIndex(function (c) { return c.dom === n.nextSibling })
                    }
                    var c
                    if (n instanceof window.Text) {
                      c = n.textContent
                    } else if (n instanceof window.Element) {
                      c = Y.Xml(n)
                    } else {
                      throw new Error('Unsupported XML Element found. Synchronization will no longer work!')
                    }
                    this.insert(pos, [c])
                    var content = this._content[pos]
                    content.dom = n
                    content.isInserted = true
                    _tryInsertDom(pos - 1)
                  })
                  Array.prototype.forEach.call(mutation.removedNodes, n => {
                    var pos = this._content.findIndex(function (c) {
                      return c.dom === n
                    })
                    this.delete(pos)
                  })
                }
              })
            })
          }
          this._domObserver = new MutationObserver(this._domObserverListener)
          this._domObserver.observe(dom, { attributes: true, childList: true })
          // In order to insert a new node, successor needs to be inserted
          // when c.dom can be inserted, try to insert the predecessors too
          var _tryInsertDom = (pos) => {
            var c = this._content[pos]
            var succ
            if (pos + 1 < this._content.length) {
              succ = this._content[pos + 1]
            } else {
              // pseudo successor
              succ = {
                isInserted: true,
                dom: null
              }
            }
            while (pos >= 0 && succ.isInserted && c.dom != null && !c.isInserted) {
              dom.insertBefore(c.dom, succ.dom)
              c.isInserted = true
              succ = c
              c = this._content[--pos]
            }
          }
          this._tryInsertDom = _tryInsertDom
          this.observe(events => {
            mutualExclude(() => {
              events.sort(function (a, b) {
                return a.index < b.index
              }).forEach(event => {
                if (event.type === 'attributeChanged') {
                  dom.setAttribute(event.name, event.value)
                } else if (event.type === 'attributeRemoved') {
                  dom.removeAttribute(event.name)
                } else if (event.type === 'childInserted') {
                  if (typeof event.node === 'string') {
                    var textNode = new window.Text(event.node)
                    this._content[event.index].dom = textNode
                    _tryInsertDom(event.index)
                  } else {
                    event.node().then(xml => {
                      return xml.getDom()
                    }).then(newNode => {
                      mutualExclude(() => {
                        // This is called async. So we have to compute the position again
                        // also mutual excluse this
                        var pos = this._content.findIndex(function (c) {
                          return c.id === event.valueId
                        })
                        if (pos >= 0) {
                          this._content[pos].dom = newNode
                          _tryInsertDom(pos)
                        }
                      })
                    })
                  }
                } else if (event.type === 'childRemoved') {
                  var d = event._content.dom
                  if (d != null) {
                    d.remove()
                  }
                  _tryInsertDom(event.index - 1)
                }
              })
            })
          })
          resolve(dom)
        })
      }
      _setDom (dom) {
        if (this.dom != null) {
          throw new Error('Only call this method if you know what you are doing ;)')
        } else if (dom.__yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
          throw new Error('Already bound to an YXml type')
        } else {
          dom.__yxml = this._model
          // tag is already set in constructor
          // set attributes
          for (var i = 0; i < dom.attributes.length; i++) {
            var attr = dom.attributes[i]
            this.attributes.set(attr.name, attr.value)
          }
          this.insert(0, Array.prototype.map.call(dom.childNodes, function (c, i) {
            if (c instanceof window.Element) {
              return Y.Xml(c)
            } else if (c instanceof window.Text) {
              return c.textContent
            } else {
              throw new Error('Unknown node type!')
            }
          }))
          Array.prototype.forEach.call(dom.childNodes, (dom, i) => {
            var c = this._content[i]
            c.dom = dom
            c.isInserted = true
          })
          this.dom = this._bindToDom(dom)
          return this.dom
        }
        return this.dom
      }
      getDom () {
        if (this.dom == null) {
          var dom = document.createElement(this.tagname)
          dom.__yxml = this
          this.attributes.keysPrimitives().forEach(key => {
            dom.setAttribute(key, this.attributes.get(key))
          })
          return new Promise((resolve) => {
            var self = this
            this.os.requestTransaction(function *() {
              var children = [] // <Promise([dom, content_i])>
              for (var i = 0; i < self._content.length; i++) {
                let c = self._content[i]
                if (c.hasOwnProperty('val')) {
                  children.push([new window.Text(c.val), c])
                } else {
                  var type = yield* this.getType(c.type)
                  children.push(type.getDom().then(function (dom) {
                    return [dom, c]
                  }))
                }
              }
              self.dom = self._bindToDom(dom)
              Promise.all(children).then(function (inserts) {
                self._mutualExclude(function () {
                  inserts.forEach(function (ins, i) {
                    // need to find position again, because this could be deleted (though this is very unlikely)
                    var pos
                    if (self._content[i] === ins[1]) {
                      // likeliest case
                      pos = i
                    } else {
                      // find content again
                      pos = self._content.findIndex(function (c) {
                        return c === ins[1]
                      })
                    }
                    if (pos >= 0) {
                      // not deleted, insert dom
                      ins[1].dom = ins[0]
                      self._tryInsertDom(pos)
                    }
                  })
                  self.dom.then(resolve)
                })
              })
            })
          })
        }
        return this.dom
      }
      observe (f) {
        this._eventListenerHandler.addEventListener(f)
      }
      unobserve (f) {
        this._eventListenerHandler.removeEventListener(f)
      }
      * _changed () {
        if (this._domObserver != null) {
          this._domObserverListener(this._domObserver.takeRecords())
        }
        yield* Y.Array.typeDefinition['class'].prototype._changed.apply(this, arguments)
      }
    }
    Y.extend('Xml', new Y.utils.CustomType({
      name: 'Xml',
      class: YXml,
      struct: 'List',
      parseArguments: function (arg) {
        if (typeof arg === 'string') {
          return [this, {
            tagname: arg
          }]
        } else if (arg instanceof window.Element) {
          return [this, {
            tagname: arg.tagName,
            dom: arg
          }]
        } else {
          throw new Error('Y.Xml requires an argument which is a string!')
        }
      },
      appendAdditionalInfo: function * (op, init) {
        var id = null
        if (op.id[0] === '_') {
          id = ['_', 'prop_' + op.id[1]]
        }
        var properties = yield* this.createType(Y.Map(), id)
        op.info = {
          tagname: init.tagname
        }
        op.requires = [properties._model] // XML dequires that properties is loaded
      },
      initType: function * YXmlInitializer (os, model, init) {
        var _content = yield* Y.Struct.List.map.call(this, model, function (op) {
          var c = {
            id: JSON.stringify(op.id)
          }
          if (op.hasOwnProperty('opContent')) {
            c.type = op.opContent
          } else {
            c.val = op.content
          }
          return c
        })
        var properties = yield* this.getType(model.requires[0]) // get the only required op
        return new YXml(os, model.id, _content, properties, model.info.tagname, init)
      }
    }))
  })
}

module.exports = extend
if (typeof Y !== 'undefined') {
  extend(Y)
}
<commit_msg>fix appendAdditionalInfo for recent yjs type bugfix<commit_after>/* global Y, MutationObserver */
'use strict'

function extend (Y) {
  Y.requestModules(['Array', 'Map']).then(function () {
    class YXml extends Y.Array.typeDefinition['class'] {
      constructor (os, _model, _content, attributes, tagname, init) {
        super(os, _model, _content)
        this.attributes = attributes
        this.dom = null
        this._domObserver = null
        this._eventListenerHandler = new Y.utils.EventListenerHandler()
        this.tagname = tagname
        if (init != null && init.dom != null) {
          this._setDom(init.dom)
        }
        super.observe(events => {
          var userEvents = []
          events.forEach(event => {
            if (event.type === 'insert') {
              userEvents.push({
                type: 'childInserted',
                index: event.index,
                node: event.value,
                valueId: event.valueId
              })
            } else if (event.type === 'delete') {
              userEvents.push({
                type: 'childRemoved',
                index: event.index,
                _content: event._content,
                value: event.value
              })
            }
          })
          if (userEvents.length > 0) {
            this._eventListenerHandler.callEventListeners(userEvents)
          }
        })
        attributes.observe(events => {
          var userEvents = []
          events.forEach(event => {
            if (event.type === 'update' || event.type === 'add') {
              userEvents.push({
                type: 'attributeChanged',
                name: event.name,
                value: event.value
              })
            } else if (event.type === 'delete') {
              userEvents.push({
                type: 'attributeRemoved',
                name: event.name
              })
            }
          })
          if (userEvents.length > 0) {
            this._eventListenerHandler.callEventListeners(userEvents)
          }
        })
      }
      _destroy () {
        if (this._domObserver != null) {
          this._domObserver.disconnect()
        }
        this._eventListenerHandler.destroy()
        this._eventListenerHandler = null
        super._destroy()
      }
      insert (pos, types) {
        if (!Array.isArray(types)) {
          throw new Error('Expected an Array of content!')
        }
        types.forEach(function (v) {
          var t = Y.utils.isTypeDefinition(v)
          if (!(v && (
                       typeof v === 'string' ||
                       (t && t[0].class === YXml)
             ))) {
            throw new Error('Expected Y.Xml type or String!')
          }
        })
        super.insert(pos, types)
      }
      // binds to a dom element
      // Only call if dom and YXml are isomorph
      _bindToDom (dom) {
        return new Promise(resolve => {
          // this function makes sure that either the
          // dom event is executed, or the yjs observer is executed
          var token = true
          var mutualExclude = f => {
            // take and process current records
            var records = this._domObserver.takeRecords()
            if (records.length > 0) {
              this._domObserverListener(records)
            }
            if (token) {
              token = false
              try {
                f()
              } catch (e) {
                // discard created records
                this._domObserver.takeRecords()
                token = true
                throw e
              }
              this._domObserver.takeRecords()
              token = true
            }
          }
          this._mutualExclude = mutualExclude
          this._domObserverListener = mutations => {
            mutualExclude(() => {
              mutations.forEach(mutation => {
                if (mutation.type === 'attributes') {
                  this.attributes.set(mutation.attributeName, mutation.target.getAttribute(mutation.attributeName))
                } else if (mutation.type === 'childList') {
                  Array.prototype.forEach.call(Array.prototype.reverse.call(mutation.addedNodes), n => {
                    // compute position
                    var pos
                    if (n.nextSibling == null) {
                      pos = this._content.length
                    } else {
                      pos = this._content.findIndex(function (c) { return c.dom === n.nextSibling })
                    }
                    var c
                    if (n instanceof window.Text) {
                      c = n.textContent
                    } else if (n instanceof window.Element) {
                      c = Y.Xml(n)
                    } else {
                      throw new Error('Unsupported XML Element found. Synchronization will no longer work!')
                    }
                    this.insert(pos, [c])
                    var content = this._content[pos]
                    content.dom = n
                    content.isInserted = true
                    _tryInsertDom(pos - 1)
                  })
                  Array.prototype.forEach.call(mutation.removedNodes, n => {
                    var pos = this._content.findIndex(function (c) {
                      return c.dom === n
                    })
                    this.delete(pos)
                  })
                }
              })
            })
          }
          this._domObserver = new MutationObserver(this._domObserverListener)
          this._domObserver.observe(dom, { attributes: true, childList: true })
          // In order to insert a new node, successor needs to be inserted
          // when c.dom can be inserted, try to insert the predecessors too
          var _tryInsertDom = (pos) => {
            var c = this._content[pos]
            var succ
            if (pos + 1 < this._content.length) {
              succ = this._content[pos + 1]
            } else {
              // pseudo successor
              succ = {
                isInserted: true,
                dom: null
              }
            }
            while (pos >= 0 && succ.isInserted && c.dom != null && !c.isInserted) {
              dom.insertBefore(c.dom, succ.dom)
              c.isInserted = true
              succ = c
              c = this._content[--pos]
            }
          }
          this._tryInsertDom = _tryInsertDom
          this.observe(events => {
            mutualExclude(() => {
              events.sort(function (a, b) {
                return a.index < b.index
              }).forEach(event => {
                if (event.type === 'attributeChanged') {
                  dom.setAttribute(event.name, event.value)
                } else if (event.type === 'attributeRemoved') {
                  dom.removeAttribute(event.name)
                } else if (event.type === 'childInserted') {
                  if (typeof event.node === 'string') {
                    var textNode = new window.Text(event.node)
                    this._content[event.index].dom = textNode
                    _tryInsertDom(event.index)
                  } else {
                    event.node().then(xml => {
                      return xml.getDom()
                    }).then(newNode => {
                      mutualExclude(() => {
                        // This is called async. So we have to compute the position again
                        // also mutual excluse this
                        var pos = this._content.findIndex(function (c) {
                          return c.id === event.valueId
                        })
                        if (pos >= 0) {
                          this._content[pos].dom = newNode
                          _tryInsertDom(pos)
                        }
                      })
                    })
                  }
                } else if (event.type === 'childRemoved') {
                  var d = event._content.dom
                  if (d != null) {
                    d.remove()
                  }
                  _tryInsertDom(event.index - 1)
                }
              })
            })
          })
          resolve(dom)
        })
      }
      _setDom (dom) {
        if (this.dom != null) {
          throw new Error('Only call this method if you know what you are doing ;)')
        } else if (dom.__yxml != null) { // TODO do i need to check this? - no.. but for dev purps..
          throw new Error('Already bound to an YXml type')
        } else {
          dom.__yxml = this._model
          // tag is already set in constructor
          // set attributes
          for (var i = 0; i < dom.attributes.length; i++) {
            var attr = dom.attributes[i]
            this.attributes.set(attr.name, attr.value)
          }
          this.insert(0, Array.prototype.map.call(dom.childNodes, function (c, i) {
            if (c instanceof window.Element) {
              return Y.Xml(c)
            } else if (c instanceof window.Text) {
              return c.textContent
            } else {
              throw new Error('Unknown node type!')
            }
          }))
          Array.prototype.forEach.call(dom.childNodes, (dom, i) => {
            var c = this._content[i]
            c.dom = dom
            c.isInserted = true
          })
          this.dom = this._bindToDom(dom)
          return this.dom
        }
        return this.dom
      }
      getDom () {
        if (this.dom == null) {
          var dom = document.createElement(this.tagname)
          dom.__yxml = this
          this.attributes.keysPrimitives().forEach(key => {
            dom.setAttribute(key, this.attributes.get(key))
          })
          return new Promise((resolve) => {
            var self = this
            this.os.requestTransaction(function *() {
              var children = [] // <Promise([dom, content_i])>
              for (var i = 0; i < self._content.length; i++) {
                let c = self._content[i]
                if (c.hasOwnProperty('val')) {
                  children.push([new window.Text(c.val), c])
                } else {
                  var type = yield* this.getType(c.type)
                  children.push(type.getDom().then(function (dom) {
                    return [dom, c]
                  }))
                }
              }
              self.dom = self._bindToDom(dom)
              Promise.all(children).then(function (inserts) {
                self._mutualExclude(function () {
                  inserts.forEach(function (ins, i) {
                    // need to find position again, because this could be deleted (though this is very unlikely)
                    var pos
                    if (self._content[i] === ins[1]) {
                      // likeliest case
                      pos = i
                    } else {
                      // find content again
                      pos = self._content.findIndex(function (c) {
                        return c === ins[1]
                      })
                    }
                    if (pos >= 0) {
                      // not deleted, insert dom
                      ins[1].dom = ins[0]
                      self._tryInsertDom(pos)
                    }
                  })
                  self.dom.then(resolve)
                })
              })
            })
          })
        }
        return this.dom
      }
      observe (f) {
        this._eventListenerHandler.addEventListener(f)
      }
      unobserve (f) {
        this._eventListenerHandler.removeEventListener(f)
      }
      * _changed () {
        if (this._domObserver != null) {
          this._domObserverListener(this._domObserver.takeRecords())
        }
        yield* Y.Array.typeDefinition['class'].prototype._changed.apply(this, arguments)
      }
    }
    Y.extend('Xml', new Y.utils.CustomType({
      name: 'Xml',
      class: YXml,
      struct: 'List',
      parseArguments: function (arg) {
        if (typeof arg === 'string') {
          return [this, {
            tagname: arg
          }]
        } else if (arg instanceof window.Element) {
          return [this, {
            tagname: arg.tagName,
            dom: arg
          }]
        } else {
          throw new Error('Y.Xml requires an argument which is a string!')
        }
      },
      appendAdditionalInfo: function * (op, init) {
        var id = null
        if (op.id[0] === '_') {
          var typestruct = Y.Map.typeDefinition.struct
          id = ['_', typestruct + '_' + 'Map_' + op.id[1]]
        }
        var properties = yield* this.createType(Y.Map(), id)
        op.info = {
          tagname: init.tagname
        }
        op.requires = [properties._model] // XML dequires that properties is loaded
      },
      initType: function * YXmlInitializer (os, model, init) {
        var _content = yield* Y.Struct.List.map.call(this, model, function (op) {
          var c = {
            id: JSON.stringify(op.id)
          }
          if (op.hasOwnProperty('opContent')) {
            c.type = op.opContent
          } else {
            c.val = op.content
          }
          return c
        })
        var properties = yield* this.getType(model.requires[0]) // get the only required op
        return new YXml(os, model.id, _content, properties, model.info.tagname, init)
      }
    }))
  })
}

module.exports = extend
if (typeof Y !== 'undefined') {
  extend(Y)
}
<|endoftext|>


<commit_before>'use strict';

var currentRoute = routes[47];

var seasons = [
	'summer',
	'winter'
];
var days = [
	'sunday',
	'monday',
	'tuesday',
	'wednesday',
	'thursday',
	'friday',
	'saturday',
];

var today = {
	season: 'winter',
	dayOfWeek: days[new Date().getDay()]
};

function initRouteTimes(currentRoute) {
	var times = currentRoute.data.times;
	var template = times.template;
	var toReturn = {};

	for (var s in seasons) {
		var seasonString = seasons[s];
		var currentSeason = times[seasonString];
		toReturn[seasonString] = {};
		for (var d in days) {
			var dayString = days[d];
			var currentSchedule = template[currentSeason[dayString]];
			toReturn[seasonString][dayString] = [];
			for (var c in currentSchedule) {
				var currentTime = currentSchedule[c];
				var tmpEnd = currentTime.end.split(':');
					tmpEnd[0] = parseInt(tmpEnd[0]);
					tmpEnd[1] = parseInt(tmpEnd[1]);
				var tmpStart = currentTime.start.split(':');
					tmpStart[0] = parseInt(tmpStart[0]);
					tmpStart[1] = parseInt(tmpStart[1]);
				var currentHours = 1 - (tmpEnd[1] - tmpStart[1]) / 60;
				    currentHours += (tmpEnd[0] - tmpStart[0]);
				var currentFrequency = 60 / currentTime.frequency; //assuming tha busses run at least 1 time per hour
				var totalTrips = currentHours * currentFrequency;
				var schedule = [
					tmpEnd[0] - Math.ceil(currentHours),
					tmpEnd[1]
				];
				for (var t = 0; t < totalTrips; t++) {
					schedule[1] += currentTime.frequency;
					if (schedule[1] > 59) {
						schedule[1] -= 60;
						schedule[0]++;
					}
					var currentDeparture = schedule.join(':');
					var a = {
						day: dayString,
						season: seasonString,
						departure: currentDeparture,
						model: currentTime
					};
					toReturn[seasonString][dayString].push(a);
				}
			}
		}
	}
	return toReturn;
}

function changeRoute(routeData, newRoute) {
	var currentRoute = routes[newRoute];
	var route = currentRoute.data.route;
	routeData.title(currentRoute.en.title);
	routeData.desc(currentRoute.en.desc);
	routeData.currentSchedule = initRouteTimes(currentRoute);
	routeData.route.removeAll();
	for (var i in route) {
		routeData.route.push(route[i]);
	}
	return filterSchedule(routeData);
}

function filterSchedule(routeData) {
	routeData.times.removeAll();
	var season = routeData.season()[0] || 'winter';
	var selectedDay = routeData.currentSchedule[season][routeData.day()];
	for (var i in selectedDay) {
		routeData.times.push(selectedDay[i]);
	}
	return routeData;
}

function BusRouteViewModel() {
	var self = this;

	self.routeData = {
		currentSchedule: {}, //data reference
		title: ko.observable(''),
		desc: ko.observable(''),
		route: ko.observableArray([]), //route, automatic
		times: ko.observableArray([]), //route times, automatic
		routeId: ko.observable(''), //route id, user editable
		day: ko.observable(today.dayOfWeek), //today, user editable
		season: ko.observableArray([]), //winter, user editable
	};

	self.days = days;

	self.changeDay = function(newDay) {
		self.routeData.day(newDay);
	};

	self.routeData.routeId.subscribe(function(newRoute) {
		changeRoute(self.routeData, newRoute);
	});

	self.routeData.day.subscribe(function() {
		filterSchedule(self.routeData);
	});
	self.routeData.season.subscribe(function() {
		filterSchedule(self.routeData);
	});

	self.route = currentRoute.data.route;

	self.isRunning = function(data) {
		if ((data.day === today.dayOfWeek) && //filter out 6/7 of list
			(data.season === today.season)) { //filter out 1/2 of list
			var now = new Date();
			now = now.getHours()*60 + now.getMinutes();
			var departure = data.departure.split(':');
			departure = parseInt(departure[0]*60) + parseInt(departure[1]);
			var difference = departure - now;
			if (difference > 0) {
				if (difference < data.model.frequency) {
					return 'bus-next';
				} else {
					return 'bus-scheduled';
				}
			} else {
				return 'bus-departed';
			}
		}
		return 'bus-none';
	};

	self.routeData.routeId('47');

	return false;
}

var busRouteViewModel = new BusRouteViewModel();
ko.applyBindings(busRouteViewModel);
<commit_msg>refractor: BusRouteViewModel functions<commit_after>'use strict';

var currentRoute = routes[47];

var seasons = [
	'summer',
	'winter'
];
var days = [
	'sunday',
	'monday',
	'tuesday',
	'wednesday',
	'thursday',
	'friday',
	'saturday',
];

var today = {
	season: 'winter',
	dayOfWeek: days[new Date().getDay()]
};

function initRouteTimes(currentRoute) {
	var times = currentRoute.data.times;
	var template = times.template;
	var toReturn = {};

	for (var s in seasons) {
		var seasonString = seasons[s];
		var currentSeason = times[seasonString];
		toReturn[seasonString] = {};
		for (var d in days) {
			var dayString = days[d];
			var currentSchedule = template[currentSeason[dayString]];
			toReturn[seasonString][dayString] = [];
			for (var c in currentSchedule) {
				var currentTime = currentSchedule[c];
				var tmpEnd = currentTime.end.split(':');
					tmpEnd[0] = parseInt(tmpEnd[0]);
					tmpEnd[1] = parseInt(tmpEnd[1]);
				var tmpStart = currentTime.start.split(':');
					tmpStart[0] = parseInt(tmpStart[0]);
					tmpStart[1] = parseInt(tmpStart[1]);
				var currentHours = 1 - (tmpEnd[1] - tmpStart[1]) / 60;
				    currentHours += (tmpEnd[0] - tmpStart[0]);
				var currentFrequency = 60 / currentTime.frequency; //assuming tha busses run at least 1 time per hour
				var totalTrips = currentHours * currentFrequency;
				var schedule = [
					tmpEnd[0] - Math.ceil(currentHours),
					tmpEnd[1]
				];
				for (var t = 0; t < totalTrips; t++) {
					schedule[1] += currentTime.frequency;
					if (schedule[1] > 59) {
						schedule[1] -= 60;
						schedule[0]++;
					}
					var currentDeparture = schedule.join(':');
					var a = {
						day: dayString,
						season: seasonString,
						departure: currentDeparture,
						model: currentTime
					};
					toReturn[seasonString][dayString].push(a);
				}
			}
		}
	}
	return toReturn;
}

function changeRoute(routeData, newRoute) {
	var currentRoute = routes[newRoute];
	var route = currentRoute.data.route;
	routeData.title(currentRoute.en.title);
	routeData.desc(currentRoute.en.desc);
	routeData.currentSchedule = initRouteTimes(currentRoute);
	routeData.route.removeAll();
	for (var i in route) {
		routeData.route.push(route[i]);
	}
	return filterSchedule(routeData);
}

function filterSchedule(routeData) {
	routeData.times.removeAll();
	var season = routeData.season()[0] || 'winter';
	var selectedDay = routeData.currentSchedule[season][routeData.day()];
	for (var i in selectedDay) {
		routeData.times.push(selectedDay[i]);
	}
	return routeData;
}

function scheduleStatus(data) {
	if ((data.day === today.dayOfWeek) && //filter out 6/7 of list
		(data.season === today.season)) { //filter out 1/2 of list
		var now = new Date();
		now = now.getHours()*60 + now.getMinutes();
		var departure = data.departure.split(':');
		departure = parseInt(departure[0]*60) + parseInt(departure[1]);
		var difference = departure - now;
		if (difference > 0) {
			if (difference < data.model.frequency) {
				return 'next';
			} else {
				return 'scheduled';
			}
		} else {
			return 'departed';
		}
	}
	return 'none';
}

function BusRouteViewModel() {
	var self = this;

	self.routeData = {
		currentSchedule: {}, //data reference
		title: ko.observable(''),
		desc: ko.observable(''),
		route: ko.observableArray([]), //route, automatic
		times: ko.observableArray([]), //route times, automatic
		routeId: ko.observable(''), //route id, user editable
		day: ko.observable(today.dayOfWeek), //today, user editable
		season: ko.observableArray([]), //winter, user editable
	};

	self.days = days;

	self.changeDay = function(newDay) {
		self.routeData.day(newDay);
	};
	self.isRunning = function(data) {
		return 'bus-' + scheduleStatus(data);
	};

	self.routeData.routeId.subscribe(function(newRoute) {
		changeRoute(self.routeData, newRoute);
	});
	self.routeData.day.subscribe(function() {
		filterSchedule(self.routeData);
	});
	self.routeData.season.subscribe(function() {
		filterSchedule(self.routeData);
	});

	self.routeData.routeId('47');

	return false;
}

var busRouteViewModel = new BusRouteViewModel();
ko.applyBindings(busRouteViewModel);
<|endoftext|>


<commit_before>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats SELECT query with complex WHERE", function() {
        const result = standardSqlFormatter.format(
            "SELECT count(*),'Column1','Testing', 'Testing Three' FROM Table1 WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) );"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  'Column1',\n" +
            "  'Testing',\n" +
            "  'Testing Three'\n" +
            "FROM\n" +
            "  Table1\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  );\n"
        );
    });

    it("formats GROUP BY and ORDER BY", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo GROUP BY some_column ORDER BY other_column;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "GROUP BY\n" +
            "  some_column\n" +
            "ORDER BY\n" +
            "  other_column;\n"
        );
    });

    it("formats LIMIT and OFFSET", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM Table1 LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  Table1\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "WHERE\n" +
            "  a > b\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "uPDATE Customers set ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "uPDATE\n" +
            "  Customers\n" +
            "set\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) As ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) As ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<commit_msg>XR-3710 Create single test for toplevel reserved words<commit_after>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats SELECT query with complex WHERE", function() {
        const result = standardSqlFormatter.format(
            "SELECT count(*),'Column1','Testing', 'Testing Three' FROM Table1 WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) );"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  'Column1',\n" +
            "  'Testing',\n" +
            "  'Testing Three'\n" +
            "FROM\n" +
            "  Table1\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  );\n"
        );
    });

    it("formats SELECT with toplevel reserved words", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo WHERE name = 'John' GROUP BY some_column " +
            "HAVING column > 10 ORDER BY other_column LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "WHERE\n" +
            "  name = 'John'\n" +
            "GROUP BY\n" +
            "  some_column\n" +
            "HAVING\n" +
            "  column > 10\n" +
            "ORDER BY\n" +
            "  other_column\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "WHERE\n" +
            "  a > b\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "uPDATE Customers set ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "uPDATE\n" +
            "  Customers\n" +
            "set\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) As ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) As ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  Button,
  Container,
  Card,
  CardHeader,
  Row,
  Col,
  Collapse,
  ListGroup,
  ListGroupItem
} from 'reactstrap';
import moment from 'moment';

function openAttachment (attachment) {
  var byteArray = new Uint8Array(attachment.content.data);
  var file = new Blob([byteArray], { type: attachment.contentType });
  var fileURL = URL.createObjectURL(file);
  window.open(fileURL);
}

const Email = ({ email, isOpen, onToggle }) => {
  let from = email.from.value[0];
  let to = email.to.value[0];
  return (
    <Card>
      <CardHeader onClick={onToggle}>
        <Row>
          <Col className="px-2" md={4}>

              {from.name && from.name.length ? from.name : from.address}

              {to.name && to.name.length ? to.name : to.address}

          </Col>
          <Col className="px-2">
            {email.subject}
          </Col>
        </Row>
      </CardHeader>
      <Collapse isOpen={isOpen}>
        <ListGroup className="list-group-flush">
          <ListGroupItem>
            From:

          </ListGroupItem>
          <ListGroupItem>
            To:

          </ListGroupItem>
          <ListGroupItem>
            Date:
            {moment(email.date).fromNow()}
          </ListGroupItem>
          <ListGroupItem>
            Subject:
            {email.subject}
          </ListGroupItem>
          <ListGroupItem hidden={email.attachments.length === 0}>
            Attachments:

              {email.attachments.map(attachment => (
                <Button size="sm" onClick={() => openAttachment(attachment)}>
                  {attachment.filename}
                </Button>
              ))}

          </ListGroupItem>
        </ListGroup>

      </Collapse>
    </Card>
  )
};

function removeTrailingSlash(url) {
  return url.replace(/\/$/, "");
}

const baseUrl = process.env.NODE_ENV === 'development'
  ? 'http://localhost:1080'
  : removeTrailingSlash(`${window.location.origin}${window.location.pathname}`);

class App extends Component {

  state = {
    emails: null,
    activeEmail: null
  };

  componentDidMount() {
      let request = {
          credentials: 'same-origin',
      };
      fetch(`${baseUrl}/api/emails`, request)
          .then(resp => resp.json())
          .then(emails => {
              this.setState({emails: emails});
          });
  }

  handleToggle = email => () => {
    if (this.state.activeEmail === email.messageId) {
      this.setState({ activeEmail: null });
    } else {
      this.setState({ activeEmail: email.messageId });
    }
  };

  render() {
    const isLoading = !this.state.emails;
    const isEmpty = !isLoading && this.state.emails.length === 0;
    const hasEmails = !isLoading && !isEmpty;
    return (
      <Container>

            Emails

        { hasEmails && this.state.emails.map(email => (
          <Email email={email}
                 isOpen={this.state.activeEmail === email.messageId}
                 onToggle={this.handleToggle(email)}
                 key={email.messageId} />
          ))
        }
        { isEmpty && (

            Empty mailbox

        ) }
      </Container>
    );
  }
}

export default App;
<commit_msg>Fix attachment link spacing when there are multiple attachments<commit_after>import React, { Component } from 'react';
import {
  Button,
  Container,
  Card,
  CardHeader,
  Row,
  Col,
  Collapse,
  ListGroup,
  ListGroupItem
} from 'reactstrap';
import moment from 'moment';

function openAttachment (attachment) {
  var byteArray = new Uint8Array(attachment.content.data);
  var file = new Blob([byteArray], { type: attachment.contentType });
  var fileURL = URL.createObjectURL(file);
  window.open(fileURL);
}

const Email = ({ email, isOpen, onToggle }) => {
  let from = email.from.value[0];
  let to = email.to.value[0];
  return (
    <Card>
      <CardHeader onClick={onToggle}>
        <Row>
          <Col className="px-2" md={4}>

              {from.name && from.name.length ? from.name : from.address}

              {to.name && to.name.length ? to.name : to.address}

          </Col>
          <Col className="px-2">
            {email.subject}
          </Col>
        </Row>
      </CardHeader>
      <Collapse isOpen={isOpen}>
        <ListGroup className="list-group-flush">
          <ListGroupItem>
            From:

          </ListGroupItem>
          <ListGroupItem>
            To:

          </ListGroupItem>
          <ListGroupItem>
            Date:
            {moment(email.date).fromNow()}
          </ListGroupItem>
          <ListGroupItem>
            Subject:
            {email.subject}
          </ListGroupItem>
          <ListGroupItem hidden={email.attachments.length === 0}>
            Attachments:

              {email.attachments.map(attachment => (
                <Button size="sm" className="mr-1" onClick={() => openAttachment(attachment)}>
                  {attachment.filename}
                </Button>
              ))}

          </ListGroupItem>
        </ListGroup>

      </Collapse>
    </Card>
  )
};

function removeTrailingSlash(url) {
  return url.replace(/\/$/, "");
}

const baseUrl = process.env.NODE_ENV === 'development'
  ? 'http://localhost:1080'
  : removeTrailingSlash(`${window.location.origin}${window.location.pathname}`);

class App extends Component {

  state = {
    emails: null,
    activeEmail: null
  };

  componentDidMount() {
      let request = {
          credentials: 'same-origin',
      };
      fetch(`${baseUrl}/api/emails`, request)
          .then(resp => resp.json())
          .then(emails => {
              this.setState({emails: emails});
          });
  }

  handleToggle = email => () => {
    if (this.state.activeEmail === email.messageId) {
      this.setState({ activeEmail: null });
    } else {
      this.setState({ activeEmail: email.messageId });
    }
  };

  render() {
    const isLoading = !this.state.emails;
    const isEmpty = !isLoading && this.state.emails.length === 0;
    const hasEmails = !isLoading && !isEmpty;
    return (
      <Container>

            Emails

        { hasEmails && this.state.emails.map(email => (
          <Email email={email}
                 isOpen={this.state.activeEmail === email.messageId}
                 onToggle={this.handleToggle(email)}
                 key={email.messageId} />
          ))
        }
        { isEmpty && (

            Empty mailbox

        ) }
      </Container>
    );
  }
}

export default App;
<|endoftext|>