<commit_before>var options,
    VK_CTRL = 1024,
    VK_SHIFT = 2048,
    actionKeys = [
        {
            id:'actionKey',
            title:'Activate Hover Zoom+',
            description:'If a key is set, Hover Zoom+ will be active only when this key is held down.'
        },
        {
            id:'hideKey',
            title:'Disable Hover Zoom+',
            description:'Holding this key down hides the zoomed picture. Use it when the picture hides elements that are also displayed on mouseover.'
        },
        {
            id:'openImageInWindowKey',
            title:'Open image in a new window',
            description:'Press this key to open the image you are currently viewing in a new window. Press this key again to close the window.'
        },
        {
            id:'openImageInTabKey',
            title:'Open image in a new tab',
            description:'Press this key to open the image you are currently viewing in a new tab. Press this key again to close the tab. Shift+key opens the image in a background tab.'
        },
        {
            id:'saveImageKey',
            title:'Save image',
            description:'Press this key to save the image you are currently viewing.'
        },
        {
            id:'fullZoomKey',
            title:'Activate full zoom',
            description:'When this key is held down, the picture is displayed using all available space. Useful for high resolution pictures only.'
        },
        {
            id:'prevImgKey',
            title:'View previous image in a gallery',
            description:'Press this key to view the previous image in a gallery.'
        },
        {
            id:'nextImgKey',
            title:'View next image in a gallery',
            description:'Press this key to view the next image in a gallery.'
        }
    ];

function getMilliseconds(ctrl) {
    var value = parseFloat(ctrl.val());
    value = isNaN(value) ? 0 : value * 1000;
    ctrl.val(value / 1000);
    return value;
}

function keyCodeToString(key) {
    var s = '';
    if (key & VK_CTRL) {
        s += 'Ctrl+';
        key &= ~VK_CTRL;
    }
    if (key & VK_SHIFT) {
        s += 'Shift+';
        key &= ~VK_SHIFT;
    }
    if (key >= 65 && key < 91) {
        s += String.fromCharCode('A'.charCodeAt(0) + key - 65);
    } else if (key >= 112 && key < 124) {
        s += 'F' + (key - 111);
    }
    return s;
}

function initActionKeys() {
    for (var i = 0; i < actionKeys.length; i++) {
        var key = actionKeys[i];
        $('
' + key.title + '
' + key.description + '
	' +
            '<select id="sel' + key.id + '" class="actionKey"/>
').appendTo($('#tableActionKeys'));
        loadKeys($('#sel' + key.id));
    }
}

function loadKeys(sel) {
    $('<option value="0">None</option>').appendTo(sel);
    if (sel.attr('id') != 'selopenImageInTabKey')
        $('<option value="16">Shift</option>').appendTo(sel);
    $('<option value="17">Ctrl</option>').appendTo(sel);
    if (navigator.appVersion.indexOf('Macintosh') > -1) {
        $('<option value="91">Command</option>').appendTo(sel);
    }
    for (var i = 65; i < 91; i++) {
        $('<option value="' + i + '">&#' + i + ';</option>').appendTo(sel);
    }
    for (var i = 112; i < 124; i++) {
        $('<option value="' + i + '">F' + (i - 111) + '</option>').appendTo(sel);
    }
    $('<option value="33">Page Up</option>').appendTo(sel);
    $('<option value="34">Page Down</option>').appendTo(sel);
    $('<option value="35">End</option>').appendTo(sel);
    $('<option value="36">Home</option>').appendTo(sel);
    $('<option value="37">Left</option>').appendTo(sel);
    $('<option value="38">Up</option>').appendTo(sel);
    $('<option value="39">Right</option>').appendTo(sel);
    $('<option value="40">Down</option>').appendTo(sel);
    $('<option value="45">Insert</option>').appendTo(sel);
    $('<option value="46">Delete</option>').appendTo(sel);
}

// Saves options to localStorage.
function saveOptions() {
    options.extensionEnabled = $('#chkExtensionEnabled')[0].checked;
    options.zoomVideos = $('#chkZoomVideos')[0].checked;
    options.muteVideos = $('#chkMuteVideos')[0].checked;
    options.mouseUnderlap = $('#chkMouseUnderlap')[0].checked;
    options.pageActionEnabled = $('#chkPageActionEnabled')[0].checked;
    options.showCaptions = $('#chkShowCaptions')[0].checked;
    options.showWhileLoading = $('#chkShowWhileLoading')[0].checked;
    options.showHighRes = $('#chkShowHighRes')[0].checked;
    options.galleriesMouseWheel = $('chkGalleriesMouseWheel')[0].checked;
    options.displayDelay = getMilliseconds($('#txtDisplayDelay'));
    options.fadeDuration = getMilliseconds($('#txtFadeDuration'));

    options.whiteListMode = $('#chkWhiteListMode')[0].checked;
    options.excludedSites = [];
    $('#selExcludedSites').find('span').each(function () {
        options.excludedSites.push($(this).text());
    });

    for (var i = 0; i < actionKeys.length; i++) {
        options[actionKeys[i].id] = parseInt($('#sel' + actionKeys[i].id).val());
    }

    options.updateNotifications = $('#chkUpdateNotifications')[0].checked;
    options.addToHistory = $('#chkAddToHistory')[0].checked;
    options.filterNSFW = $('#chkFilterNSFW')[0].checked;
    options.alwaysPreload = $('#chkAlwaysPreload')[0].checked;
    options.enableGalleries = $('#chkEnableGalleries')[0].checked;
    options.enableStats = $('#chkEnableStats')[0].checked;
    options.picturesOpacity = $('#txtPicturesOpacity')[0].value / 100;

    localStorage.options = JSON.stringify(options);
    sendOptions(options);
    restoreOptions();
    $('#messages').clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500);
    return false;
}

// Restores options from localStorage.
function restoreOptions() {
    options = loadOptions();

    $('#chkExtensionEnabled')[0].checked = options.extensionEnabled;
    $('#chkZoomVideos')[0].checked = options.zoomVideos;
    $('#chkMuteVideos')[0].checked = options.muteVideos;
    $('#chkMouseUnderlap')[0].checked = options.mouseUnderlap;
    $('#chkPageActionEnabled')[0].checked = options.pageActionEnabled;
    $('#chkShowCaptions')[0].checked = options.showCaptions;
    $('#chkShowWhileLoading')[0].checked = options.showWhileLoading;
    $('#chkShowHighRes')[0].checked = options.showHighRes;
	$('#chkGalleriesMouseWheel')[0].checked = options.galleriesMouseWheel;
    $('#txtDisplayDelay').val((options.displayDelay || 0) / 1000);
    $('#txtFadeDuration').val((options.fadeDuration || 0) / 1000);

    $('#chkWhiteListMode')[0].checked = options.whiteListMode;
    $('#selExcludedSites').empty();
    for (var i = 0; i < options.excludedSites.length; i++) {
        appendExcludedSite(options.excludedSites[i]);
    }

    for (var i = 0; i < actionKeys.length; i++) {
        $('#sel' + actionKeys[i].id).val(options[actionKeys[i].id]);
    }

    $('#chkUpdateNotifications')[0].checked = options.updateNotifications;
    $('#chkAddToHistory')[0].checked = options.addToHistory;
    $('#chkFilterNSFW')[0].checked = options.filterNSFW;

    $('#chkAlwaysPreload')[0].checked = options.alwaysPreload;
    $('#chkEnableGalleries')[0].checked = options.enableGalleries;
    $('#chkEnableStats')[0].checked = options.enableStats;
    $('#txtPicturesOpacity').val(options.picturesOpacity * 100);

    $('input:checked').trigger('gumby.check');
    return false;
}

function btnAddExcludedSiteOnClick() {
    var field = $('#txtAddExcludedSite');
    var site = field.val().trim().toLowerCase().replace(/.*:\/\//, '');
    if (site) appendExcludedSite(site);
    field.val('').focus();
    return false;
}

function appendExcludedSite(site) {
    $('
 ' + site + '
').appendTo('#selExcludedSites').find('a').on('click', btnRemoveExcludedSiteOnClick);
}

function btnRemoveExcludedSiteOnClick() {
    $(this).parent().remove();
    return false;
}

function selKeyOnChange(event) {
    var currSel = $(event.target);
    if (currSel.val() != '0') {
        $('.actionKey').each(function () {
            if (!$(this).is(currSel) && $(this).val() == currSel.val()) {
                $(this).val('0').effect("highlight", {color:'red'}, 5000);
            }
        });
    }
}

function chkWhiteListModeOnChange() {
    if ($('#chkWhiteListMode')[0].checked) {
        $('#dis-enabled').text('enabled');
    } else {
        $('#dis-enabled').text('disabled');
    }
}

function chkAddToHistoryModeOnChange() {
    if ($('#chkAddToHistory')[0].checked) {
        chrome.permissions.contains({permissions: ['history']}, function (granted) {
            if (!granted) {
                chrome.permissions.request({permissions: ['history']}, function (granted) {
                    if (!granted) {
                        $("#chkAddToHistory").trigger('gumby.uncheck');
                    }
                });
            }
        });
    }
}

function txtPicturesOpacityOnChange() {
    var value = parseInt(this.value);
    if (isNaN(value)) value = 100;
    if (value < 0) value = 0;
    if (value > 100) value = 100;
    this.value = value;
}

function onMessage(message, sender, callback) {
    switch (message.action) {
        case 'optionsChanged':
            restoreOptions();
            break;
    }
}

$(function () {
    initActionKeys();
    $('#btnSave').click(saveOptions);
    $('#btnReset').click(restoreOptions);
    $('#chkWhiteListMode').parent().on('gumby.onChange', chkWhiteListModeOnChange);
    $('#chkAddToHistory').parent().on('gumby.onChange', chkAddToHistoryModeOnChange);
    $('#txtPicturesOpacity').change(txtPicturesOpacityOnChange);
    $('.actionKey').change(selKeyOnChange);
    $('#btnAddExcludedSite').click(btnAddExcludedSiteOnClick);
    $('#btnRemoveExcludedSite').click(btnRemoveExcludedSiteOnClick);
    $('#aShowUpdateNotification').click(showUpdateNotification);
    restoreOptions();
    chrome.runtime.onMessage.addListener(onMessage);
    $('#versionNumber').text(chrome.app.getDetails().version);
});
<commit_msg>fix for #26<commit_after>var options,
    VK_CTRL = 1024,
    VK_SHIFT = 2048,
    actionKeys = [
        {
            id:'actionKey',
            title:'Activate Hover Zoom+',
            description:'If a key is set, Hover Zoom+ will be active only when this key is held down.'
        },
        {
            id:'hideKey',
            title:'Disable Hover Zoom+',
            description:'Holding this key down hides the zoomed picture. Use it when the picture hides elements that are also displayed on mouseover.'
        },
        {
            id:'openImageInWindowKey',
            title:'Open image in a new window',
            description:'Press this key to open the image you are currently viewing in a new window. Press this key again to close the window.'
        },
        {
            id:'openImageInTabKey',
            title:'Open image in a new tab',
            description:'Press this key to open the image you are currently viewing in a new tab. Press this key again to close the tab. Shift+key opens the image in a background tab.'
        },
        {
            id:'saveImageKey',
            title:'Save image',
            description:'Press this key to save the image you are currently viewing.'
        },
        {
            id:'fullZoomKey',
            title:'Activate full zoom',
            description:'When this key is held down, the picture is displayed using all available space. Useful for high resolution pictures only.'
        },
        {
            id:'prevImgKey',
            title:'View previous image in a gallery',
            description:'Press this key to view the previous image in a gallery.'
        },
        {
            id:'nextImgKey',
            title:'View next image in a gallery',
            description:'Press this key to view the next image in a gallery.'
        }
    ];

function getMilliseconds(ctrl) {
    var value = parseFloat(ctrl.val());
    value = isNaN(value) ? 0 : value * 1000;
    ctrl.val(value / 1000);
    return value;
}

function keyCodeToString(key) {
    var s = '';
    if (key & VK_CTRL) {
        s += 'Ctrl+';
        key &= ~VK_CTRL;
    }
    if (key & VK_SHIFT) {
        s += 'Shift+';
        key &= ~VK_SHIFT;
    }
    if (key >= 65 && key < 91) {
        s += String.fromCharCode('A'.charCodeAt(0) + key - 65);
    } else if (key >= 112 && key < 124) {
        s += 'F' + (key - 111);
    }
    return s;
}

function initActionKeys() {
    for (var i = 0; i < actionKeys.length; i++) {
        var key = actionKeys[i];
        $('
' + key.title + '
' + key.description + '
	' +
            '<select id="sel' + key.id + '" class="actionKey"/>
').appendTo($('#tableActionKeys'));
        loadKeys($('#sel' + key.id));
    }
}

function loadKeys(sel) {
    $('<option value="0">None</option>').appendTo(sel);
    if (sel.attr('id') != 'selopenImageInTabKey')
        $('<option value="16">Shift</option>').appendTo(sel);
    $('<option value="17">Ctrl</option>').appendTo(sel);
    if (navigator.appVersion.indexOf('Macintosh') > -1) {
        $('<option value="91">Command</option>').appendTo(sel);
    }
    for (var i = 65; i < 91; i++) {
        $('<option value="' + i + '">&#' + i + ';</option>').appendTo(sel);
    }
    for (var i = 112; i < 124; i++) {
        $('<option value="' + i + '">F' + (i - 111) + '</option>').appendTo(sel);
    }
    $('<option value="33">Page Up</option>').appendTo(sel);
    $('<option value="34">Page Down</option>').appendTo(sel);
    $('<option value="35">End</option>').appendTo(sel);
    $('<option value="36">Home</option>').appendTo(sel);
    $('<option value="37">Left</option>').appendTo(sel);
    $('<option value="38">Up</option>').appendTo(sel);
    $('<option value="39">Right</option>').appendTo(sel);
    $('<option value="40">Down</option>').appendTo(sel);
    $('<option value="45">Insert</option>').appendTo(sel);
    $('<option value="46">Delete</option>').appendTo(sel);
}

// Saves options to localStorage.
function saveOptions() {
    options.extensionEnabled = $('#chkExtensionEnabled')[0].checked;
    options.zoomVideos = $('#chkZoomVideos')[0].checked;
    options.muteVideos = $('#chkMuteVideos')[0].checked;
    options.mouseUnderlap = $('#chkMouseUnderlap')[0].checked;
    options.pageActionEnabled = $('#chkPageActionEnabled')[0].checked;
    options.showCaptions = $('#chkShowCaptions')[0].checked;
    options.showWhileLoading = $('#chkShowWhileLoading')[0].checked;
    options.showHighRes = $('#chkShowHighRes')[0].checked;
    options.galleriesMouseWheel = $('#chkGalleriesMouseWheel')[0].checked;
    options.displayDelay = getMilliseconds($('#txtDisplayDelay'));
    options.fadeDuration = getMilliseconds($('#txtFadeDuration'));

    options.whiteListMode = $('#chkWhiteListMode')[0].checked;
    options.excludedSites = [];
    $('#selExcludedSites').find('span').each(function () {
        options.excludedSites.push($(this).text());
    });

    for (var i = 0; i < actionKeys.length; i++) {
        options[actionKeys[i].id] = parseInt($('#sel' + actionKeys[i].id).val());
    }

    options.updateNotifications = $('#chkUpdateNotifications')[0].checked;
    options.addToHistory = $('#chkAddToHistory')[0].checked;
    options.filterNSFW = $('#chkFilterNSFW')[0].checked;
    options.alwaysPreload = $('#chkAlwaysPreload')[0].checked;
    options.enableGalleries = $('#chkEnableGalleries')[0].checked;
    options.enableStats = $('#chkEnableStats')[0].checked;
    options.picturesOpacity = $('#txtPicturesOpacity')[0].value / 100;

    localStorage.options = JSON.stringify(options);
    sendOptions(options);
    restoreOptions();
    $('#messages').clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500);
    return false;
}

// Restores options from localStorage.
function restoreOptions() {
    options = loadOptions();

    $('#chkExtensionEnabled')[0].checked = options.extensionEnabled;
    $('#chkZoomVideos')[0].checked = options.zoomVideos;
    $('#chkMuteVideos')[0].checked = options.muteVideos;
    $('#chkMouseUnderlap')[0].checked = options.mouseUnderlap;
    $('#chkPageActionEnabled')[0].checked = options.pageActionEnabled;
    $('#chkShowCaptions')[0].checked = options.showCaptions;
    $('#chkShowWhileLoading')[0].checked = options.showWhileLoading;
    $('#chkShowHighRes')[0].checked = options.showHighRes;
	$('#chkGalleriesMouseWheel')[0].checked = options.galleriesMouseWheel;
    $('#txtDisplayDelay').val((options.displayDelay || 0) / 1000);
    $('#txtFadeDuration').val((options.fadeDuration || 0) / 1000);

    $('#chkWhiteListMode')[0].checked = options.whiteListMode;
    $('#selExcludedSites').empty();
    for (var i = 0; i < options.excludedSites.length; i++) {
        appendExcludedSite(options.excludedSites[i]);
    }

    for (var i = 0; i < actionKeys.length; i++) {
        $('#sel' + actionKeys[i].id).val(options[actionKeys[i].id]);
    }

    $('#chkUpdateNotifications')[0].checked = options.updateNotifications;
    $('#chkAddToHistory')[0].checked = options.addToHistory;
    $('#chkFilterNSFW')[0].checked = options.filterNSFW;

    $('#chkAlwaysPreload')[0].checked = options.alwaysPreload;
    $('#chkEnableGalleries')[0].checked = options.enableGalleries;
    $('#chkEnableStats')[0].checked = options.enableStats;
    $('#txtPicturesOpacity').val(options.picturesOpacity * 100);

    $('input:checked').trigger('gumby.check');
    return false;
}

function btnAddExcludedSiteOnClick() {
    var field = $('#txtAddExcludedSite');
    var site = field.val().trim().toLowerCase().replace(/.*:\/\//, '');
    if (site) appendExcludedSite(site);
    field.val('').focus();
    return false;
}

function appendExcludedSite(site) {
    $('
 ' + site + '
').appendTo('#selExcludedSites').find('a').on('click', btnRemoveExcludedSiteOnClick);
}

function btnRemoveExcludedSiteOnClick() {
    $(this).parent().remove();
    return false;
}

function selKeyOnChange(event) {
    var currSel = $(event.target);
    if (currSel.val() != '0') {
        $('.actionKey').each(function () {
            if (!$(this).is(currSel) && $(this).val() == currSel.val()) {
                $(this).val('0').effect("highlight", {color:'red'}, 5000);
            }
        });
    }
}

function chkWhiteListModeOnChange() {
    if ($('#chkWhiteListMode')[0].checked) {
        $('#dis-enabled').text('enabled');
    } else {
        $('#dis-enabled').text('disabled');
    }
}

function chkAddToHistoryModeOnChange() {
    if ($('#chkAddToHistory')[0].checked) {
        chrome.permissions.contains({permissions: ['history']}, function (granted) {
            if (!granted) {
                chrome.permissions.request({permissions: ['history']}, function (granted) {
                    if (!granted) {
                        $("#chkAddToHistory").trigger('gumby.uncheck');
                    }
                });
            }
        });
    }
}

function txtPicturesOpacityOnChange() {
    var value = parseInt(this.value);
    if (isNaN(value)) value = 100;
    if (value < 0) value = 0;
    if (value > 100) value = 100;
    this.value = value;
}

function onMessage(message, sender, callback) {
    switch (message.action) {
        case 'optionsChanged':
            restoreOptions();
            break;
    }
}

$(function () {
    initActionKeys();
    $('#btnSave').click(saveOptions);
    $('#btnReset').click(restoreOptions);
    $('#chkWhiteListMode').parent().on('gumby.onChange', chkWhiteListModeOnChange);
    $('#chkAddToHistory').parent().on('gumby.onChange', chkAddToHistoryModeOnChange);
    $('#txtPicturesOpacity').change(txtPicturesOpacityOnChange);
    $('.actionKey').change(selKeyOnChange);
    $('#btnAddExcludedSite').click(btnAddExcludedSiteOnClick);
    $('#btnRemoveExcludedSite').click(btnRemoveExcludedSiteOnClick);
    $('#aShowUpdateNotification').click(showUpdateNotification);
    restoreOptions();
    chrome.runtime.onMessage.addListener(onMessage);
    $('#versionNumber').text(chrome.app.getDetails().version);
});
<|endoftext|>


<commit_before>var express = require('express'),
	archive = require('./routes/archive');

var app = express();
app.get('/archive', archive.findAll);
app.get('/archive/:id', archive.findById);
app.listen(3000);
console.log('Listening on port 3000');
<commit_msg>Added rethinkdb test code<commit_after>var express = require('express'),
	archive = require('./routes/archive');

r = require('rethinkdb');

var connection = r.connect( {host: '127.0.0.1', port: 28015});
connection.then(function(conn) {
//	r.dbCreate('archivedb').run(conn, function(err, res) {
//        	if (err) throw err;
//        	console.log(res);
//	});
//	r.db('archivedb').tableCreate('archives').run(conn, function(err, res) {
  //      	if (err) throw err;
//        	console.log(JSON.stringify(res, null, 2));
//	});
	r.db('archivedb').table('archives').insert({url: 'http://edina.ac.uk'}).run(conn, function(err, res) {
		if (err) throw err;
		console.log(res);
	});
}).error(function(err) {
	console.log(err);
});

var app = express();
app.get('/archive', archive.findAll);
app.get('/archive/:id', archive.findById);
app.listen(3000);
console.log('Listening on port 3000');
<|endoftext|>


<commit_before>// Avoid `console` errors in browsers that lack a console.
(function() {
    var method;
    var noop = function () {};
    var methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
        'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
        'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
        'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
    ];
    var length = methods.length;
    var console = (window.console = window.console || {});

    while (length--) {
        method = methods[length];

        // Only stub undefined methods.
        if (!console[method]) {
            console[method] = noop;
        }
    }
}());

// Place any jQuery/helper plugins in here.
<commit_msg>Delete h5bp plugins.js.<commit_after><|endoftext|>


<commit_before>var bodyParser = require('body-parser');
var express = require('express');
var app = express();

// SETUP STUFF
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));
app.set('views', './views');
app.set('view engine', 'ejs');

// AUTH SUCCESS CALLBACK
app.post('/auth/success', function(req, res) {
  var accessData = req.body.accessData || '{}';
  res.render('auth/success', { accessData: accessData });
});

// LAUNCH
var port = process.env.PORT || 3069;
var appName = process.env.APP_NAME || 'Legislation Lab App';
var server = app.listen(port, function () {
  console.log('%s backend running at http://localhost:%s', appName, port);
});
<commit_msg>App name fix<commit_after>var bodyParser = require('body-parser');
var express = require('express');
var app = express();

// SETUP STUFF
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));
app.set('views', './views');
app.set('view engine', 'ejs');

// AUTH SUCCESS CALLBACK
app.post('/auth/success', function(req, res) {
  var accessData = req.body.accessData || '{}';
  res.render('auth/success', { accessData: accessData });
});

// LAUNCH
var port = process.env.PORT || 3069;
var appName = process.env.APP_NAME || 'Application';
var server = app.listen(port, function () {
  console.log('%s backend running at http://localhost:%s', appName, port);
});
<|endoftext|>


<commit_before>(function () {
    'use strict';

    var MongoClient = require('mongodb').MongoClient, restify = require('restify'), assert = require('assert');//, ObjectId = require('mongodb').ObjectID;

    //Needs to impliment aggregate stuff (https://docs.mongodb.com/manual/reference/operator/aggregation/)
    //Need to impliment mapreduce stuff
    //Need to impliment projection stuff

    /*the following is for the internal db (accesible as a registered UAB user only)*/

    //Good source for operators:
    // https://docs.mongodb.com/manual/reference/operator/
    //I am making sure to not allow any operators that can change the documents
    // this is to ensure that this remains a query only server

    var acceptedRegex = "", sanatize, j, grabDbName, grabDocument,
            accepted$ = [
        "all", "and", "bitsAllClear", "bitsAllSet", "bitsAnyClear",
        "bitsAnySet", "collStats", "comment", "elemMatch",
        "eq", "exists", "explain", "geoIntersects", "geoWithin", "gt",
        "gte", "hint", "in", "limit", "lte", "match", "max", "maxScan",
        "maxTimeMS", "meta", "min", "mod", "natural", "ne", "near",
        "nearShpere", "nin", "nor", "not", "or", "orderby", "query",
        "regex", "returnKey", "showDiskLoc", "size", "skip", "slice",
        "snapshot", "text", "type", "where"
    ];

    for (j = 0; j < accepted$.length; j += 1) {
        acceptedRegex += "^\\$" + accepted$[j] + "$|";
    }
    acceptedRegex = acceptedRegex.replace(/\|$/, "");
    acceptedRegex = new RegExp(acceptedRegex, 'i');

    //define the query sanatize function
    //sanatize the query
    sanatize = function (query) {
        var keys, i;
        if (typeof query === "object") {
            keys = Object.keys(query);
            for (i = 0; i < keys.length; i += 1) {
                if (keys[i].match(/^\s*\$/)) {
                    //straight up kill it if it is not an accepted type
                    if (!keys[i].match(acceptedRegex)) {
                        delete query[keys[i]];
                    }
                } else if (typeof query[keys[i]] === "object") {
                    query[keys[i]] = sanatize(query[keys[i]]);
                }
            }
        } else {
            //If it is not an object then just query it all
            // should probably generate an error message
            query = {};
        }
        return query;
    };

    grabDbName = function (request, response) {
        var myDbName = request.params.db_name, query, fields, sort,
                collectionName = request.params.collection_name, url;

        //Deal with the objects
        // myDbName = myDbName || 'kinome';
        url = 'mongodb://localhost:27017/' + myDbName;
        console.log('db name:', myDbName);
        request.query.find = request.query.find || "{}";
        request.query.fields = request.query.fields || "-1";
        request.query.sort = request.query.sort || "[]";

        request.query.find = decodeURIComponent(request.query.find);
        request.query.fields = decodeURIComponent(request.query.fields);
        request.query.sort = decodeURIComponent(request.query.sort);

        //Objectify them
        query = JSON.parse(request.query.find);
        query = sanatize(query);
        fields = JSON.parse(request.query.fields);
        sort = JSON.parse(request.query.sort);

        //if there is a sort option, sanatize it
        if (sort.length > 0) {
            sanatize(sort);
        }

        //Start up connection and run the query
        MongoClient.connect(url, function (err, db) {
            assert.equal(null, err);
            var spec;
            var collection = db.collection(collectionName);
            console.log("Connected successfully to server");

            //options
            var limit = request.query.limit * 1 || 9000000; //9 mil more than enough
            var skip = request.query.skip * 1 || 0;
            var maxTimeMS = request.query.maxTimeMS * 1 || 1000 * 60 * 60; //1 hr

            //special stuff
            console.log(query, fields, sort);
            if (fields === -1) {
                spec = collection.find(query, {
                    limit: limit,
                    skip: skip,
                    maxTimeMS: maxTimeMS
                });
            } else {
                spec = collection.find(query, sanatize(fields), {
                    limit: limit,
                    skip: skip,
                    maxTimeMS: maxTimeMS
                });
            }

            //run the sort operation and then return the final result
            spec.sort(sort).toArray(function (err, docs) {
                assert.equal(err, null);
                console.log('Found docs');
               // console.log("Found the following records", docs);
                response.send(docs);
            });
        });
    };

    grabDocument = function (request, response) {
        var myDbName = request.params.db_name, fields,
                collectionName = request.params.collection_name,
                query = {id: decodeURIComponent(request.params.doc_id)}, url;

        //Deal with the objects
        // myDbName = myDbName || 'kinome';
        url = 'mongodb://localhost:27017/' + myDbName;
        request.query.fields = request.query.fields || "-1";
        request.query.fields = decodeURIComponent(request.query.fields);

        //Objectify them
        query = sanatize(query);
        fields = JSON.parse(request.query.fields);

        //Start up connection and run the query
        MongoClient.connect(url, function (err, db) {
            assert.equal(null, err);
            var spec;
            var collection = db.collection(collectionName);
            console.log("Connected successfully to server");

            //options
            var maxTimeMS = request.query.maxTimeMS * 1 || 1000 * 60 * 60; //1 hr

            //special stuff
            console.log(query, myDbName, collectionName);
            if (fields === -1) {
                spec = collection.find({"_id": query.id}, {
                    maxTimeMS: maxTimeMS
                });
            } else {
                spec = collection.find({"_id": query.id}, sanatize(fields), {
                    maxTimeMS: maxTimeMS
                });
            }

            //run the sort operation and then return the final result
            spec.toArray(function (err, docs) {
                assert.equal(err, null);
                console.log('Found docs');
                //console.log("Found the following records", docs);
                //Only return the first object
                if (docs[0]) {
                    response.send(docs[0]);
                } else {
                    response.send(undefined);
                }
            });
        });
    };

    //sets up the server stuff
    var server1 = restify.createServer({
        accept: ['application/json', 'image/tif', 'image/png', 'text/plain']
    });
    server1.use(restify.queryParser());
    server1.use(restify.CORS({}));

    server1.get(/\/img\/kinome\/?.*/, restify.serveStatic({
        directory: "/var/www"
    }));
    server1.get(/\/file\/?.*/, restify.serveStatic({
        directory: "/var/www/global_files"
    }));

    server1.get(/\/uabfile\/?.*/, restify.serveStatic({
        directory: "/var/www/internal_files"
    }));

    //http://138.26.31.155:8000/img/kinome/631308613_W1_F1_T200_P154_I1313_A30.tif

    /*the following is for the internal db (accesible as a registered UAB user only)*/
    server1.get("/db/:db_name/:collection_name", grabDbName);
    server1.get("/db/:db_name/:collection_name/:doc_id", grabDocument);
    server1.listen(8000, function () {
        console.log('%s listening at %s', server1.name, server1.url);
    });

    // var server2 = restify.createServer({
    //     accept: ['application/json', 'image/tif', 'image/png']
    // });
    // server2.use(restify.queryParser());
    // server2.use(restify.CORS({}));

    // server2.get(/\/img\/kinome\/?.*/, restify.serveStatic({
    //     directory: "./server_imgs"
    // }));

    // /*The following is for the external db*/
    // server2.get("/1.0.0/:collection_name", grabDbName);
    // server2.get("/1.0.0/:collection_name/:doc_id", grabDocument);
    // server2.listen(8080, function () {
    //     console.log('%s listening at %s', server2.name, server2.url);
    // });

}());
<commit_msg>update basic server.js file<commit_after>(function () {
    'use strict';

    /**************************************************************************/
    // Set this to false for installations, specifically created to allow
        // access to UAB SAML authentication mechanism for privileged data
        // access and editing.
    var addUABAuth = false;
    /**************************************************************************/

    /*The following is for the external db*/

    var restify = require('restify'),
        serverObj = {
            '1.0.0': require("./src_api/server_library.js"),
            '2.0.0': require("./src_api/api_v2.0.0.js")
        },
        auth,
        users,
        cookieParser;

    if (addUABAuth) {
        auth = require("./src_auth/saml_auth2.js");
        users = require("./src_auth/acceptedUsers.js");
        cookieParser = require('restify-cookies');
    } else {
        // This will just return a basic use object for use with the v2 API
        users = {};
        users.permission = require("./src_auth/acceptedUsers.js").permission;
    }

    //, ObjectId = require('mongodb').ObjectID;

    //Needs to impliment aggregate stuff (https://docs.mongodb.com/manual/reference/operator/aggregation/)
    //Need to impliment mapreduce stuff
    //Need to impliment projection stuff

    //Good source for operators:
    // https://docs.mongodb.com/manual/reference/operator/
    //I am making sure to not allow any operators that can change the documents
    // this is to ensure that this remains a query only server

    //sets up the server stuff
    // var server1 = restify.createServer({
    //     accept: ['application/json', 'image/tif', 'image/png']
    // });
    // server1.use(restify.queryParser());
    // server1.use(restify.CORS({}));

    // server1.get(/\/img\/kinome\/?.*/, restify.serveStatic({
    //     directory: "/var/www"
    // }));

    // //http://138.26.31.155:8000/img/kinome/631308613_W1_F1_T200_P154_I1313_A30.tif

    // /*the following is for the internal db (accesible as a registered UAB user only)*/
    // server1.get("/db/:db_name/:collection_name", grabDbName);
    // server1.get("/db/:db_name/:collection_name/:doc_id", grabDocument);
    // server1.listen(8000, function () {
    //     console.log('%s listening at %s', server1.name, server1.url);
    // });

    /*

        This file is for the general external server. Use this to make your own
        server.

    */

    var server = restify.createServer({
        accept: ['application/json']
    });
    server.use(restify.queryParser());
    server.use(restify.bodyParser());
    server.use(restify.CORS({credentials: true}));

    if (addUABAuth) {
        server.use(cookieParser.parse);
    }

    //static serving of flat files
    server.get(/\/img\/kinome\/?.*/, restify.serveStatic({
        directory: "/var/www"
    }));
    server.get(/\/file\/?.*/, restify.serveStatic({
        directory: "/var/www/global_files"
    }));

    /*The following is for the internal db*/
    server.get("/1.0.0/:collection_name", serverObj["1.0.0"].grabDbName);
    server.get("/1.0.0/:collection_name/:doc_id", serverObj["1.0.0"].grabDocument);

    /////////////////
    // API v 2.0.0 //
    /////////////////

    // databse/collection methods
    server.get("/2.0.0/:database/:collection", serverObj["2.0.0"].list);
    server.post("/2.0.0/:database/:collection", serverObj["2.0.0"].post);

    // database/collection/document methods
    server.get("/2.0.0/:database/:collection/:doc_id", serverObj["2.0.0"].get);
    server.patch("/2.0.0/:database/:collection/:doc_id", serverObj["2.0.0"].patch);

    // check permissions (if no auth mechanism is in place, this will return
    //  default settings in src_auth/acceptedUsers)
    server.get('/auth/:database/:collection', function (req, res, next) {
        users.permission(req).then(function (perms) {
            res.send(perms);
        }).catch(function (err) {
            var code = "500";
            console.log(err);
            if (err.message.match(/^(\d{3}):/)) {
                code = err.message.replace(/^(\d{3}):[\s\S]+/, "$1");
            }

            //send it back with the code and the object
            res.send(code * 1, {
                error: {
                    code: code,
                    message: err.message
                }
            });
        });
        return next();
    });

    //lists the permissions (temporary)
    server.get("/test", function (req, res, next) {
        users.permission(req).then(function (perms) {
            if (perms.email) {
                res.send({logged_in: true, data: [perms], message: "Based on Cookies this user is logged in."});
            } else {
                res.send({logged_in: false, resp: perms, message: "Based on Cookies this user is not logged in."});
            }
            return next();
        });
    });

    /////////////////
    // v 2.0.0 END //
    /////////////////

    // Login parameters
    if (addUABAuth) {
        server.get("/login", auth.login);
        server.post("/login/callback", auth.post_token(users.new_login));
        server.get("/metadata", auth.metadata);
    }

    // Listen
    server.listen(8000, function () {
        console.log('%s listening at %s', server.name, server.url);
    });

}());
<|endoftext|>


<commit_before>/*
 VisSynthWeb server - a long polling server for remote control

 This is a simple HTTP file server, with some special features:

 -REST-like long-polling pipeline
  GET requests to any location below the feeds/ path are delayed until information is placed there via a PUT request.
  A requesting client can idle on a GET request, until some remote control puts commands via PUT there. The requesting
  client is then answered immediately and the information dropped. Two web clients can send commands and data to each other
  using two path locations.

 -REST-like value store
  PUT requests to any location below the saves/ path store the data given as the request body to disk

 -Recorder tool
  the pathes recorder/start and recorder/stop allow to run and stop a hardwired avconv tool process to record the current
  screen content.

*/

var http = require('http');
var ws = require('ws');
var fs=require('fs');
var path=require('path');
var child_process = require('child_process');

var data={};
var pending={};

//var recorder_cmd="avconv -f x11grab -r 25 -s 1600x900 -i :0.0+0,0 -vcodec libx264 -pre lossless_ultrafast -threads 4 -y video.mov";
//var recorder_cmd="avconv";
//var recorder_args="-f x11grab -r 25 -s 1600x900 -i :0.0+0,0 -vcodec libx264 -pre lossless_ultrafast -threads 4 -y video.mov".split(" ");
var recorder_cmd="gst-launch-0.10";
var recorder_args="-e ximagesrc use-damage=0 ! ffmpegcolorspace ! nv_omx_h264enc bitrate=16000000 ! qtmux ! filesink location={FILENAME}";
var recorder=false;

// HTTP server for delivering the client and handle server-side commands
var server=http.createServer(function (req, res) {

  console.log("Serving "+req.url);

  var parts = req.url.split('?');
  parts[0]=parts[0].substring(1); // strip trailing /
  var key=parts[0];

  if(fs.existsSync(key) && fs.statSync(key).isFile() && key.indexOf("..")==-1)
  {
    res.setHeader("Content-Type", "text/html");
    var instream=fs.createReadStream(key);
    instream.pipe(res);
  }
  else
    res.end();
});

// WebSocket server to forward command requests between the clients
var wss=new ws.Server({server:server});
wss.on('connection', function connection(ws) {
  // var location = url.parse(ws.upgradeReq.url, true);
  // you might use location.query.access_token to authenticate or share sessions
  // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
  ws.on('message', function incoming(message) {
    var packet=JSON.parse(message);
    var method=packet.method, path=packet.path, data=packet.data;
    //console.log('WebSocket: '+method+' '+path);
    var parts = path.split('?');
    var key=parts[0].substring(1); // strip trailing /

    // allow opt-in
    if(method=='get')
    {
      if(!pending[key]) pending[key]=[];
      pending[key].push(ws);
      console.log('WebSocket opted in for '+key);
    }
    else if(key.match(/saves\/.*/))
    {
      // if it denotes a file in saves/, store it to disk
      fs.writeFileSync(key,data);
      console.log(key+' stored.');
    }
    else if(key.match(/recorder\/.*/))
    {
      if(!recorder && key.match(/recorder\/start/)){
        var args=recorder_args.replace('{FILENAME}',"recorded/"+Math.random()+".mov").split(' ');
        recorder=child_process.spawn(recorder_cmd,args, {stdio:'inherit'});
      }
      if(recorder && key.match(/recorder\/stop/)) {
        recorder.kill('SIGINT');
        recorder=false;
      }
    }
    else if(key.match(/shutdown/))
      child_process.spawn('sh',['shutdown.sh'], {stdio:'inherit'});
    else if(key.match(/restart/))
      child_process.spawn('sh',['run_chrome.sh'], {stdio:'inherit'});
    else if(key.match(/feeds\/.*/))
    {
      // if it denotes a feed in feeds/ answer pending requests for this key
      if(pending[key])
      {
        for(var i in pending[key])
        {
          var target_ws=pending[key][i];
          if(target_ws.readyState!=1) // still open?
            delete pending[key][i]; // socket is gone, remove listener (should not happen)
          else if(target_ws!=ws) // still open and not the sender itself (no echo!)
            target_ws.send(message);
        }
        //delete pending[key];
      }
    }
    else
      console.log('Invalid Websocket path:'+path);
  });
  ws.on('close',function(){
    // remove all opts for this socket
    for (key in pending)
      for (i in pending[key])
        if(pending[key][i]==ws)
        {
          console.log('WebSocket opted out for '+key);
          pending[key].splice(i,1);
        }
  });
});

var port=8082;
server.listen(port);
console.log("Listening at:%s", port);

<commit_msg>server.js: Content-Type Header mit simpler MIME-Tabelle erzeugt<commit_after>/*
 VisSynthWeb server - a long polling server for remote control

 This is a simple HTTP file server, with some special features:

 -REST-like long-polling pipeline
  GET requests to any location below the feeds/ path are delayed until information is placed there via a PUT request.
  A requesting client can idle on a GET request, until some remote control puts commands via PUT there. The requesting
  client is then answered immediately and the information dropped. Two web clients can send commands and data to each other
  using two path locations.

 -REST-like value store
  PUT requests to any location below the saves/ path store the data given as the request body to disk

 -Recorder tool
  the pathes recorder/start and recorder/stop allow to run and stop a hardwired avconv tool process to record the current
  screen content.

*/

var http = require('http');
var ws = require('ws');
var fs=require('fs');
var path=require('path');
var child_process = require('child_process');

var mime_types={
	'.html' : 'text/html',
	'.js'   : 'application/javascript',
	'.json' : 'application/json',
	'.svg'  : 'image/svg+xml',
	'.css'  : 'text/css',
	'.png'  : 'image/png',
	'.jpg'  : 'image/jpg'
};
var data={};
var pending={};

//var recorder_cmd="avconv -f x11grab -r 25 -s 1600x900 -i :0.0+0,0 -vcodec libx264 -pre lossless_ultrafast -threads 4 -y video.mov";
//var recorder_cmd="avconv";
//var recorder_args="-f x11grab -r 25 -s 1600x900 -i :0.0+0,0 -vcodec libx264 -pre lossless_ultrafast -threads 4 -y video.mov".split(" ");
var recorder_cmd="gst-launch-0.10";
var recorder_args="-e ximagesrc use-damage=0 ! ffmpegcolorspace ! nv_omx_h264enc bitrate=16000000 ! qtmux ! filesink location={FILENAME}";
var recorder=false;

// HTTP server for delivering the client and handle server-side commands
var server=http.createServer(function (req, res) {

  console.log("Serving "+req.url);

  var parts = req.url.split('?');
  parts[0]=parts[0].substring(1); // strip trailing /
  var key=parts[0];

  if(fs.existsSync(key) && fs.statSync(key).isFile() && key.indexOf("..")==-1)
  {
    var n = key.lastIndexOf('.');
    var suffix = key.substring(n);
    res.setHeader("Content-Type", mime_types[suffix]);
    var instream=fs.createReadStream(key);
    instream.pipe(res);
  }
  else
    res.end();
});

// WebSocket server to forward command requests between the clients
var wss=new ws.Server({server:server});
wss.on('connection', function connection(ws) {
  // var location = url.parse(ws.upgradeReq.url, true);
  // you might use location.query.access_token to authenticate or share sessions
  // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
  ws.on('message', function incoming(message) {
    var packet=JSON.parse(message);
    var method=packet.method, path=packet.path, data=packet.data;
    //console.log('WebSocket: '+method+' '+path);
    var parts = path.split('?');
    var key=parts[0].substring(1); // strip trailing /

    // allow opt-in
    if(method=='get')
    {
      if(!pending[key]) pending[key]=[];
      pending[key].push(ws);
      console.log('WebSocket opted in for '+key);
    }
    else if(key.match(/saves\/.*/))
    {
      // if it denotes a file in saves/, store it to disk
      fs.writeFileSync(key,data);
      console.log(key+' stored.');
    }
    else if(key.match(/recorder\/.*/))
    {
      if(!recorder && key.match(/recorder\/start/)){
        var args=recorder_args.replace('{FILENAME}',"recorded/"+Math.random()+".mov").split(' ');
        recorder=child_process.spawn(recorder_cmd,args, {stdio:'inherit'});
      }
      if(recorder && key.match(/recorder\/stop/)) {
        recorder.kill('SIGINT');
        recorder=false;
      }
    }
    else if(key.match(/shutdown/))
      child_process.spawn('sh',['shutdown.sh'], {stdio:'inherit'});
    else if(key.match(/restart/))
      child_process.spawn('sh',['run_chrome.sh'], {stdio:'inherit'});
    else if(key.match(/feeds\/.*/))
    {
      // if it denotes a feed in feeds/ answer pending requests for this key
      if(pending[key])
      {
        for(var i in pending[key])
        {
          var target_ws=pending[key][i];
          if(target_ws.readyState!=1) // still open?
            delete pending[key][i]; // socket is gone, remove listener (should not happen)
          else if(target_ws!=ws) // still open and not the sender itself (no echo!)
            target_ws.send(message);
        }
        //delete pending[key];
      }
    }
    else
      console.log('Invalid Websocket path:'+path);
  });
  ws.on('close',function(){
    // remove all opts for this socket
    for (key in pending)
      for (i in pending[key])
        if(pending[key][i]==ws)
        {
          console.log('WebSocket opted out for '+key);
          pending[key].splice(i,1);
        }
  });
});

var port=8082;
server.listen(port);
console.log("Listening at:%s", port);

<|endoftext|>


<commit_before>// Main application initializer.
// Eventually should become part of NLib.

'use strict';

/*global N*/

// stdlib
var fs = require('fs');

// 3rd-party
var async   = require('nlib').Vendor.Async;
var fstools = require('nlib').Vendor.FsTools;

// internal
var readConfig = require('./app/utils').readConfig;

////////////////////////////////////////////////////////////////////////////////

module.exports = function (next) {
  var tmpdir, config, environment;

  try {
    // read configs from all appliations
    config = readConfig(N.runtime.apps);
    // create temporary dir for styles
    tmpdir = fstools.tmpdir();
    fs.mkdirSync(tmpdir);
  } catch (err) {
    next(err);
    return;
  }

  // Prepare Mincer environment
  N.runtime.assets = {
    environment:  require('./app/mincer').init(tmpdir, config, N.runtime.apps),
    manifest:     {},
    bin:          {}
  };

  // schedule files cleanup upon normal exit
  process.on('exit', function (code) {
    if (0 !== +code) {
      console.warn("Unclean exit. Bundled files left in '" + tmpdir + "'");
      return;
    }

    try {
      console.warn("Removing '" + tmpdir + "'...");
      fstools.removeSync(tmpdir);
    } catch (err) {
      console.warn("Failed remove '" + tmpdir + "'... " + String(err));
    }
  });

  async.series([
    async.apply(require('./app/assets'), tmpdir, config),
    async.apply(require('./app/styles'), tmpdir, config),

    async.apply(require('./app/i18n'), tmpdir, config),

    async.apply(require('./app/views'), tmpdir, config),
    async.apply(require('./app/client'), tmpdir, config),
    async.apply(require('./app/server'), tmpdir, config),

    async.apply(require('./app/bundle'), tmpdir, config)
  ], next);
};
<commit_msg>Add app init overall timer<commit_after>// Main application initializer.
// Eventually should become part of NLib.

'use strict';

/*global N*/

// stdlib
var fs = require('fs');

// 3rd-party
var async   = require('nlib').Vendor.Async;
var fstools = require('nlib').Vendor.FsTools;

// internal
var readConfig = require('./app/utils').readConfig;
var stopWatch  = require('./app/utils').stopWatch;

////////////////////////////////////////////////////////////////////////////////

module.exports = function (callback) {
  var timer = stopWatch(), tmpdir, config, environment;

  try {
    // read configs from all appliations
    config = readConfig(N.runtime.apps);
    // create temporary dir for styles
    tmpdir = fstools.tmpdir();
    fs.mkdirSync(tmpdir);
  } catch (err) {
    next(err);
    return;
  }

  // Prepare Mincer environment
  N.runtime.assets = {
    environment:  require('./app/mincer').init(tmpdir, config, N.runtime.apps),
    manifest:     {},
    bin:          {}
  };

  // schedule files cleanup upon normal exit
  process.on('exit', function (code) {
    if (0 !== +code) {
      console.warn("Unclean exit. Bundled files left in '" + tmpdir + "'");
      return;
    }

    try {
      console.warn("Removing '" + tmpdir + "'...");
      fstools.removeSync(tmpdir);
    } catch (err) {
      console.warn("Failed remove '" + tmpdir + "'... " + String(err));
    }
  });

  async.series([
    async.apply(require('./app/assets'), tmpdir, config),
    async.apply(require('./app/styles'), tmpdir, config),

    async.apply(require('./app/i18n'), tmpdir, config),

    async.apply(require('./app/views'), tmpdir, config),
    async.apply(require('./app/client'), tmpdir, config),
    async.apply(require('./app/server'), tmpdir, config),

    async.apply(require('./app/bundle'), tmpdir, config)
  ], function (err) {
    N.logger.debug('App initialized ' + timer.elapsed);
    callback(err);
  });
};
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 *
 * Copyright (c) 2009 Aptana, Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * ***** END LICENSE BLOCK ***** */

var Relationships = {
    normalizeModelName: function(related_model_name)
    {
        var plural = ActiveSupport.camelize(related_model_name, true);
        var singular = ActiveSupport.Inflector.singularize(plural) || plural;
        return singular || plural;
    },
    normalizeForeignKey: function(foreign_key, related_model_name)
    {
        var plural = ActiveSupport.underscore(related_model_name).toLowerCase();
        var singular = ActiveSupport.Inflector.singularize(plural) || plural;
        if (!foreign_key || typeof(foreign_key) === 'undefined')
        {
            return (singular || plural) + '_id';
        }
        else
        {
            return foreign_key;
        }
    }
};
ActiveRecord.Relationships = Relationships;<commit_msg>fixed inflector bug<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 *
 * Copyright (c) 2009 Aptana, Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * ***** END LICENSE BLOCK ***** */

var Relationships = {
    normalizeModelName: function(related_model_name)
    {
        var plural = ActiveSupport.camelize(related_model_name, true);
        var singular = ActiveSupport.camelize(ActiveSupport.Inflector.singularize(plural) || plural,true);
        return singular || plural;
    },
    normalizeForeignKey: function(foreign_key, related_model_name)
    {
        var plural = ActiveSupport.underscore(related_model_name).toLowerCase();
        var singular = ActiveSupport.Inflector.singularize(plural) || plural;
        if (!foreign_key || typeof(foreign_key) === 'undefined')
        {
            return (singular || plural) + '_id';
        }
        else
        {
            return foreign_key;
        }
    }
};
ActiveRecord.Relationships = Relationships;<|endoftext|>


<commit_before>/* eslint-disable no-undef */
import React from 'react';
import PropTypes from 'prop-types';
import createClass from 'create-react-class';
import FilterMixin from '../src/FilterMixin';
import Menu from 'rc-menu';
import OptGroup from '../src/OptGroup';
import Option from '../src/Option';
import { render, mount } from 'enzyme';

describe('FilterMixin', () => {
  const Select = createClass({
    propTypes: {
      value: PropTypes.any,
      inputValue: PropTypes.string,
    },

    mixins: [FilterMixin],

    getInitialState() {
      const value = this.props.value ? [{ key: this.props.value }] : null;
      return {
        inputValue: this.props.inputValue,
        value,
      };
    },

    render() {
      return (
        <Menu>
          {this.renderFilterOptions(this.state.inputValue)}
        </Menu>
      );
    },
  });

  describe('renderFilterOptionsFromChildren', () => {
    const filterFn = (input, child) => {
      return child.props.value.includes(input);
    };

    it('renders options correctly', () => {
      const wrapper = render(
        <Select inputValue="1">
          <OptGroup label="group1">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </OptGroup>
          <OptGroup label="group2">
            <Option value="3" disabled>3</Option>
            <Option value="4">4</Option>
          </OptGroup>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('set label as key for OptGroup', () => {
      const wrapper = mount(
        <Select>
          <OptGroup key="group1">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </OptGroup>
        </Select>
      );

      expect(wrapper.find('MenuItemGroup').props().title).toBe('group1');
    });

    it('filters children by inputValue', () => {
      const wrapper = render(
        <Select inputValue="1" filterOption={filterFn}>
          <Option value="1">1</Option>
          <Option value="2">2</Option>
          <Option value="11" disabled>11</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('renders not found when search result is empty', () => {
      const wrapper = render(
        <Select inputValue="3" filterOption={filterFn} notFoundContent="not found">
          <Option value="1">1</Option>
          <Option value="2">2</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('warns on invalid children', () => {
      const Foo = () =>
foo
;
      spyOn(console, 'error');
      mount(
        <Select>
          <Foo value="1" />
        </Select>
      );
      expect(console.error.calls.count()).toBe(1);
      expect(console.error.calls.argsFor(0)[0]).toContain(
        'the children of `Select` should be `Select.Option` or `Select.OptGroup`, ' +
          `instead of \`Foo\`.`
      );
    });

    it('filterOption could be true as described in default value', () => {
      const wrapper = render(
        <Select inputValue="3" filterOption>
          <Option value="1">1</Option>
          <Option value="2">2</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    describe('tag mode', () => {
      it('renders unlisted item in value', () => {
        const wrapper = render(
          <Select tags value="3">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </Select>
        );

        expect(wrapper).toMatchSnapshot();
      });

      it('renders search value when not fount', () => {
        const wrapper = render(
          <Select tags value="22" inputValue="2" filterOption={filterFn}>
            <Option value="1">1</Option>
          </Select>
        );

        expect(wrapper).toMatchSnapshot();
      });
    });
  });
});
<commit_msg>Use jest.spyOn<commit_after>/* eslint-disable no-undef */
import React from 'react';
import PropTypes from 'prop-types';
import createClass from 'create-react-class';
import FilterMixin from '../src/FilterMixin';
import Menu from 'rc-menu';
import OptGroup from '../src/OptGroup';
import Option from '../src/Option';
import { render, mount } from 'enzyme';

describe('FilterMixin', () => {
  const Select = createClass({
    propTypes: {
      value: PropTypes.any,
      inputValue: PropTypes.string,
    },

    mixins: [FilterMixin],

    getInitialState() {
      const value = this.props.value ? [{ key: this.props.value }] : null;
      return {
        inputValue: this.props.inputValue,
        value,
      };
    },

    render() {
      return (
        <Menu>
          {this.renderFilterOptions(this.state.inputValue)}
        </Menu>
      );
    },
  });

  describe('renderFilterOptionsFromChildren', () => {
    const filterFn = (input, child) => {
      return child.props.value.includes(input);
    };

    it('renders options correctly', () => {
      const wrapper = render(
        <Select inputValue="1">
          <OptGroup label="group1">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </OptGroup>
          <OptGroup label="group2">
            <Option value="3" disabled>3</Option>
            <Option value="4">4</Option>
          </OptGroup>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('set label as key for OptGroup', () => {
      const wrapper = mount(
        <Select>
          <OptGroup key="group1">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </OptGroup>
        </Select>
      );

      expect(wrapper.find('MenuItemGroup').props().title).toBe('group1');
    });

    it('filters children by inputValue', () => {
      const wrapper = render(
        <Select inputValue="1" filterOption={filterFn}>
          <Option value="1">1</Option>
          <Option value="2">2</Option>
          <Option value="11" disabled>11</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('renders not found when search result is empty', () => {
      const wrapper = render(
        <Select inputValue="3" filterOption={filterFn} notFoundContent="not found">
          <Option value="1">1</Option>
          <Option value="2">2</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    it('warns on invalid children', () => {
      const Foo = () =>
foo
;
      const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
      mount(
        <Select>
          <Foo value="1" />
        </Select>
      );
      expect(spy.mock.calls.length).toBe(1);
      expect(spy.mock.calls[0][0]).toContain(
        'the children of `Select` should be `Select.Option` or `Select.OptGroup`, ' +
          `instead of \`Foo\`.`
      );
      spy.mockRestore();
    });

    it('filterOption could be true as described in default value', () => {
      const wrapper = render(
        <Select inputValue="3" filterOption>
          <Option value="1">1</Option>
          <Option value="2">2</Option>
        </Select>
      );

      expect(wrapper).toMatchSnapshot();
    });

    describe('tag mode', () => {
      it('renders unlisted item in value', () => {
        const wrapper = render(
          <Select tags value="3">
            <Option value="1">1</Option>
            <Option value="2">2</Option>
          </Select>
        );

        expect(wrapper).toMatchSnapshot();
      });

      it('renders search value when not fount', () => {
        const wrapper = render(
          <Select tags value="22" inputValue="2" filterOption={filterFn}>
            <Option value="1">1</Option>
          </Select>
        );

        expect(wrapper).toMatchSnapshot();
      });
    });
  });
});
<|endoftext|>


<commit_before>define([
    'underscore',
    'knockout',
    'knockout-mapping',
    'uuid',
    'mapbox-gl',
    'mapbox-gl-draw',
    'geojson-extent',
    'geojsonhint',
    'viewmodels/card-component',
    'views/components/map',
    'views/components/cards/select-feature-layers',
    'text!templates/views/components/cards/map-popup.htm',
    'bindings/chosen',
    'bindings/codemirror',
    'codemirror/mode/javascript/javascript'
], function(_, ko, koMapping, uuid, mapboxgl, MapboxDraw, geojsonExtent, geojsonhint, CardComponentViewModel, MapComponentViewModel, selectFeatureLayersFactory, popupTemplate) {
    var viewModel = function(params) {
        var self = this;
        var widgets = [];
        var padding = 40;
        var drawFeatures;
        var resourceId = params.tile ? params.tile.resourceinstance_id : '';

        this.newNodeId = null;
        this.featureLookup = {};
        this.selectedFeatureIds = ko.observableArray();
        this.geoJSONString = ko.observable();
        this.draw = null;
        this.setDrawTool = function(tool) {
            if (tool) self.draw.changeMode(tool);
        };

        CardComponentViewModel.apply(this, [params]);

        var configJSON = self.card.model.configJSON();
        var selectSource = configJSON.selectSource;
        var selectFeatureLayers = selectFeatureLayersFactory(resourceId, selectSource);
        this.selectText = configJSON.selectText || "Select drawing";

        this.setSelectLayersVisibility = function(visibility) {
            var map = self.map();
            if (map) {
                selectFeatureLayers.forEach(function(layer) {
                    map.setLayoutProperty(
                        layer.id,
                        'visibility',
                        visibility ? 'visible' : 'none'
                    );
                });
            }
        };

        if (self.form && self.tile) self.card.widgets().forEach(function(widget) {
            var id = widget.node_id();
            var type = self.form.nodeLookup[id].datatype();
            if (type === 'geojson-feature-collection') {
                widgets.push(widget);
                self.featureLookup[id] = {
                    features: ko.computed(function() {
                        var value = koMapping.toJS(self.tile.data[id]);
                        if (value) return value.features;
                        else return [];
                    }),
                    selectedTool: ko.observable()
                };
                self.featureLookup[id].selectedTool.subscribe(function(tool) {
                    if (self.draw) {
                        if (tool === '') {
                            self.draw.trash();
                            self.draw.changeMode('simple_select');
                        } else if (tool) {
                            _.each(self.featureLookup, function(value, key) {
                                if (key !== id) {
                                    value.selectedTool(null);
                                }
                            });
                            self.newNodeId = id;
                        }
                        self.setDrawTool(tool);
                    }
                });
            }
        });

        this.selectedTool = ko.pureComputed(function() {
            var tool;
            _.find(self.featureLookup, function(value) {
                var selectedTool = value.selectedTool();
                if (selectedTool) tool = selectedTool;
            });
            return tool;
        });

        this.editing = ko.pureComputed(function() {
            return !!(self.selectedFeatureIds().length > 0 || self.selectedTool());
        });

        this.updateTiles = function() {
            var featureCollection = self.draw.getAll();
            _.each(self.featureLookup, function(value) {
                value.selectedTool(null);
            });
            widgets.forEach(function(widget) {
                var id = widget.node_id();
                var features = [];
                featureCollection.features.forEach(function(feature){
                    if (feature.properties.nodeId === id) features.push(feature);
                });
                if (ko.isObservable(self.tile.data[id])) {
                    self.tile.data[id]({
                        type: 'FeatureCollection',
                        features: features
                    });
                } else {
                    self.tile.data[id].features(features);
                }
            });
        };

        var getDrawFeatures = function() {
            var drawFeatures = [];
            widgets.forEach(function(widget) {
                var id = widget.node_id();
                var featureCollection = koMapping.toJS(self.tile.data[id]);
                if (featureCollection) {
                    featureCollection.features.forEach(function(feature) {
                        if (!feature.id) {
                            feature.id = uuid.generate();
                        }
                        feature.properties.nodeId = id;
                    });
                    drawFeatures = drawFeatures.concat(featureCollection.features);
                }
            });
            return drawFeatures;
        };
        drawFeatures = getDrawFeatures();

        if (drawFeatures.length > 0) {
            params.bounds = geojsonExtent({
                type: 'FeatureCollection',
                features: drawFeatures
            });
            params.fitBoundsOptions = { padding: {top: padding, left: padding + 200, bottom: padding, right: padding + 200} };
        }
        params.activeTab = 'editor';
        params.sources = {
            "geojson-editor-data": {
                "type": "geojson",
                "data": {
                    "type": "FeatureCollection",
                    "features": []
                }
            }
        };
        if (!params.layers) {
            params.layers = [];
        }
        params.layers = params.layers.concat(selectFeatureLayers, [{
            "id": "geojson-editor-polygon-fill",
            "type": "fill",
            "filter": ["==", "$type", "Polygon"],
            "paint": {
                "fill-color": "#3bb2d0",
                "fill-outline-color": "#3bb2d0",
                "fill-opacity": 0.1
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-polygon-stroke",
            "type": "line",
            "filter": ["==", "$type", "Polygon"],
            "layout": {
                "line-cap": "round",
                "line-join": "round"
            },
            "paint": {
                "line-color": "#3bb2d0",
                "line-width": 2
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-line",
            "type": "line",
            "filter": ["==", "$type", "LineString"],
            "layout": {
                "line-cap": "round",
                "line-join": "round"
            },
            "paint": {
                "line-color": "#3bb2d0",
                "line-width": 2
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-point-point-stroke",
            "type": "circle",
            "filter": ["==", "$type", "Point"],
            "paint": {
                "circle-radius": 5,
                "circle-opacity": 1,
                "circle-color": "#fff"
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-point",
            "type": "circle",
            "filter": ["==", "$type", "Point"],
            "paint": {
                "circle-radius": 3,
                "circle-color": "#3bb2d0"
            },
            "source": "geojson-editor-data"
        }]);

        MapComponentViewModel.apply(this, [params]);

        this.isFeatureClickable = function(feature) {
            if (self.selectedTool()) return false;
            return feature.properties.resourceinstanceid;
        };

        this.deleteFeature = function(feature) {
            if (self.draw) {
                self.draw.delete(feature.id);
                self.updateTiles();
            }
        };

        this.editFeature = function(feature) {
            if (self.draw) {
                self.draw.changeMode('simple_select', {
                    featureIds: [feature.id]
                });
                self.selectedFeatureIds([feature.id]);
                _.each(self.featureLookup, function(value) {
                    value.selectedTool(null);
                });
            }
        };

        this.updateLayers = function(layers) {
            var map = self.map();
            var style = map.getStyle();
            style.layers = self.draw ? layers.concat(self.draw.options.styles) : layers;
            map.setStyle(style);
        };

        this.fitFeatures = function(features) {
            var map = self.map();
            var bounds = geojsonExtent({
                type: 'FeatureCollection',
                features: features
            });
            var camera = map.cameraForBounds(bounds, { padding: padding });
            map.jumpTo(camera);
        };

        this.editGeoJSON = function(features, nodeId) {
            var geoJSONString = JSON.stringify({
                type: 'FeatureCollection',
                features: features
            }, null, '   ');
            this.geoJSONString(geoJSONString);
            self.newNodeId = nodeId;
        };
        this.geoJSONString.subscribe(function(geoJSONString) {
            var map = self.map();
            if (geoJSONString === undefined) {
                setupDraw(map);
            } else if (self.draw) {
                map.removeControl(self.draw);
                self.draw = undefined;
                self.selectedFeatureIds([]);
            }
        });
        this.geoJSONErrors = ko.pureComputed(function() {
            var geoJSONString = self.geoJSONString();
            var hint = geojsonhint.hint(geoJSONString);
            var errors = [];
            hint.forEach(function(item) {
                if (item.level !== 'message') {
                    errors.push(item);
                }
            });
            return errors;
        }).extend({ rateLimit: 50 });
        var geoJSONLayerData = ko.pureComputed(function() {
            var geoJSONString = self.geoJSONString();
            var geoJSONErrors = self.geoJSONErrors();
            if (geoJSONErrors.length === 0) return JSON.parse(geoJSONString);
            else return {
                type: 'FeatureCollection',
                features: []
            };
        }).extend({ rateLimit: 100 });
        geoJSONLayerData.subscribe(function(data) {
            var map = self.map();
            map.getSource('geojson-editor-data').setData(data);
        });
        this.updateGeoJSON = function() {
            if (self.geoJSONErrors().length === 0) {
                var geoJSON = JSON.parse(this.geoJSONString());
                geoJSON.features.forEach(function(feature) {
                    if (!feature.id) feature.id = uuid.generate();
                    if (!feature.properties) feature.properties = {};
                    feature.properties.nodeId = self.newNodeId;
                });
                if (ko.isObservable(self.tile.data[self.newNodeId])) {
                    self.tile.data[self.newNodeId](geoJSON);
                } else {
                    self.tile.data[self.newNodeId].features(geoJSON.features);
                }
                self.geoJSONString(undefined);
            }
        };

        this.drawModeChange = function() {
            return;
        };

        var setupDraw = function(map) {
            var modes = MapboxDraw.modes;
            modes.static = {
                onSetup: function() {
                    this.setActionableState();
                    return {};
                },
                toDisplayFeatures: function(state, geojson, display) {
                    display(geojson);
                }
            };
            self.draw = new MapboxDraw({
                displayControlsDefault: false,
                modes: modes
            });
            map.addControl(self.draw);
            self.draw.set({
                type: 'FeatureCollection',
                features: getDrawFeatures()
            });
            map.on('draw.create', function(e) {
                e.features.forEach(function(feature) {
                    self.draw.setFeatureProperty(feature.id, 'nodeId', self.newNodeId);
                });
                self.updateTiles();
            });
            map.on('draw.update', self.updateTiles);
            map.on('draw.delete', self.updateTiles);
            map.on('draw.modechange', function(e) {
                self.drawModeChange(e);
                self.updateTiles();
            });
            map.on('draw.selectionchange', function(e) {
                self.selectedFeatureIds(e.features.map(function(feature) {
                    return feature.id;
                }));
                if (e.features.length > 0) {
                    _.each(self.featureLookup, function(value) {
                        value.selectedTool(null);
                    });
                }
            });

            self.form.on('tile-reset', function() {
                self.draw.set({
                    type: 'FeatureCollection',
                    features: getDrawFeatures()
                });
                _.each(self.featureLookup, function(value) {
                    if (value.selectedTool()) value.selectedTool('');
                });
            });

            map.on('draw.modechange', function() {
                self.setSelectLayersVisibility(false);
            });

            map.on('draw.selectionchange', function() {
                self.setSelectLayersVisibility(false);
            });
        };

        this.map.subscribe(setupDraw);

        if (!params.additionalDrawOptions) {
            params.additionalDrawOptions = [];
        }

        self.card.widgets().forEach(function(widget) {
            if (widget.config.geometryTypes) {
                widget.drawTools = ko.pureComputed(function() {
                    var options = [{
                        value: '',
                        text: ''
                    }];
                    options = options.concat(widget.config.geometryTypes().map(function(type) {
                        var option = {};
                        switch (type.id) {
                        case 'Point':
                            option.value = 'draw_point';
                            option.text = 'Add point';
                            break;
                        case 'Line':
                            option.value = 'draw_line_string';
                            option.text = 'Add line';
                            break;
                        case 'Polygon':
                            option.value = 'draw_polygon';
                            option.text = 'Add polygon';
                            break;
                        }
                        return option;
                    }));
                    if (selectSource) {
                        options.push({
                            value: "select_feature",
                            text: self.selectText
                        });
                    }
                    options = options.concat(params.additionalDrawOptions);
                    return options;
                });
            }
        });

        this.isFeatureClickable = function(feature) {
            var tool = self.selectedTool();
            if (tool && tool !== 'select_feature') return false;
            return feature.properties.resourceinstanceid;
        };

        this.geoJSONString.subscribe(function() {
            self.setSelectLayersVisibility(false);
        });

        this.popupTemplate = popupTemplate;

        this.setDrawTool = function(tool) {
            var showSelectLayers = (tool === 'select_feature');
            self.setSelectLayersVisibility(showSelectLayers);
            if (showSelectLayers) {
                self.draw.changeMode('simple_select');
                self.selectedFeatureIds([]);
            } else if (tool) self.draw.changeMode(tool);
        };

        self.isSelectable = function(feature) {
            var selectLayerIds = selectFeatureLayers.map(function(layer) {
                return layer.id;
            });
            return selectLayerIds.indexOf(feature.layer.id) >= 0;
        };

        self.selectFeature = function(feature) {
            var newFeature = {
                "id": uuid.generate(),
                "type": "Feature",
                "properties": {
                    "nodeId": self.newNodeId
                },
                "geometry": JSON.parse(feature.properties.geojson)
            };
            self.draw.add(newFeature);
            self.updateTiles();
            self.popup.remove();
            self.editFeature(newFeature);
        };
    };
    ko.components.register('map-card', {
        viewModel: viewModel,
        template: {
            require: 'text!templates/views/components/cards/map.htm'
        }
    });
    return viewModel;
});
<commit_msg>refactors map card<commit_after>define([
    'underscore',
    'knockout',
    'knockout-mapping',
    'uuid',
    'mapbox-gl',
    'mapbox-gl-draw',
    'geojson-extent',
    'geojsonhint',
    'viewmodels/card-component',
    'views/components/map',
    'views/components/cards/select-feature-layers',
    'text!templates/views/components/cards/map-popup.htm',
    'bindings/chosen',
    'bindings/codemirror',
    'codemirror/mode/javascript/javascript'
], function(_, ko, koMapping, uuid, mapboxgl, MapboxDraw, geojsonExtent, geojsonhint, CardComponentViewModel, MapComponentViewModel, selectFeatureLayersFactory, popupTemplate) {
    var viewModel = function(params) {
        var self = this;
        var widgets = [];
        var padding = 40;
        var drawFeatures;
        var resourceId = params.tile ? params.tile.resourceinstance_id : '';

        this.newNodeId = null;
        this.featureLookup = {};
        this.selectedFeatureIds = ko.observableArray();
        this.geoJSONString = ko.observable();
        this.draw = null;

        CardComponentViewModel.apply(this, [params]);

        var configJSON = self.card.model.configJSON();
        var selectSource = configJSON.selectSource;
        var selectFeatureLayers = selectFeatureLayersFactory(resourceId, selectSource);
        this.selectText = configJSON.selectText || "Select drawing";

        this.setSelectLayersVisibility = function(visibility) {
            var map = self.map();
            if (map) {
                selectFeatureLayers.forEach(function(layer) {
                    map.setLayoutProperty(
                        layer.id,
                        'visibility',
                        visibility ? 'visible' : 'none'
                    );
                });
            }
        };

        this.setDrawTool = function(tool) {
            var showSelectLayers = (tool === 'select_feature');
            self.setSelectLayersVisibility(showSelectLayers);
            if (showSelectLayers) {
                self.draw.changeMode('simple_select');
                self.selectedFeatureIds([]);
            } else if (tool) self.draw.changeMode(tool);
        };

        if (self.form && self.tile) self.card.widgets().forEach(function(widget) {
            var id = widget.node_id();
            var type = self.form.nodeLookup[id].datatype();
            if (type === 'geojson-feature-collection') {
                widgets.push(widget);
                self.featureLookup[id] = {
                    features: ko.computed(function() {
                        var value = koMapping.toJS(self.tile.data[id]);
                        if (value) return value.features;
                        else return [];
                    }),
                    selectedTool: ko.observable()
                };
                self.featureLookup[id].selectedTool.subscribe(function(tool) {
                    if (self.draw) {
                        if (tool === '') {
                            self.draw.trash();
                            self.draw.changeMode('simple_select');
                        } else if (tool) {
                            _.each(self.featureLookup, function(value, key) {
                                if (key !== id) {
                                    value.selectedTool(null);
                                }
                            });
                            self.newNodeId = id;
                        }
                        self.setDrawTool(tool);
                    }
                });
            }
        });

        this.selectedTool = ko.pureComputed(function() {
            var tool;
            _.find(self.featureLookup, function(value) {
                var selectedTool = value.selectedTool();
                if (selectedTool) tool = selectedTool;
            });
            return tool;
        });

        this.editing = ko.pureComputed(function() {
            return !!(self.selectedFeatureIds().length > 0 || self.selectedTool());
        });

        this.updateTiles = function() {
            var featureCollection = self.draw.getAll();
            _.each(self.featureLookup, function(value) {
                value.selectedTool(null);
            });
            widgets.forEach(function(widget) {
                var id = widget.node_id();
                var features = [];
                featureCollection.features.forEach(function(feature){
                    if (feature.properties.nodeId === id) features.push(feature);
                });
                if (ko.isObservable(self.tile.data[id])) {
                    self.tile.data[id]({
                        type: 'FeatureCollection',
                        features: features
                    });
                } else {
                    self.tile.data[id].features(features);
                }
            });
        };

        var getDrawFeatures = function() {
            var drawFeatures = [];
            widgets.forEach(function(widget) {
                var id = widget.node_id();
                var featureCollection = koMapping.toJS(self.tile.data[id]);
                if (featureCollection) {
                    featureCollection.features.forEach(function(feature) {
                        if (!feature.id) {
                            feature.id = uuid.generate();
                        }
                        feature.properties.nodeId = id;
                    });
                    drawFeatures = drawFeatures.concat(featureCollection.features);
                }
            });
            return drawFeatures;
        };
        drawFeatures = getDrawFeatures();

        if (drawFeatures.length > 0) {
            params.bounds = geojsonExtent({
                type: 'FeatureCollection',
                features: drawFeatures
            });
            params.fitBoundsOptions = { padding: {top: padding, left: padding + 200, bottom: padding, right: padding + 200} };
        }
        params.activeTab = 'editor';
        params.sources = {
            "geojson-editor-data": {
                "type": "geojson",
                "data": {
                    "type": "FeatureCollection",
                    "features": []
                }
            }
        };
        if (!params.layers) {
            params.layers = [];
        }
        params.layers = params.layers.concat(selectFeatureLayers, [{
            "id": "geojson-editor-polygon-fill",
            "type": "fill",
            "filter": ["==", "$type", "Polygon"],
            "paint": {
                "fill-color": "#3bb2d0",
                "fill-outline-color": "#3bb2d0",
                "fill-opacity": 0.1
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-polygon-stroke",
            "type": "line",
            "filter": ["==", "$type", "Polygon"],
            "layout": {
                "line-cap": "round",
                "line-join": "round"
            },
            "paint": {
                "line-color": "#3bb2d0",
                "line-width": 2
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-line",
            "type": "line",
            "filter": ["==", "$type", "LineString"],
            "layout": {
                "line-cap": "round",
                "line-join": "round"
            },
            "paint": {
                "line-color": "#3bb2d0",
                "line-width": 2
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-point-point-stroke",
            "type": "circle",
            "filter": ["==", "$type", "Point"],
            "paint": {
                "circle-radius": 5,
                "circle-opacity": 1,
                "circle-color": "#fff"
            },
            "source": "geojson-editor-data"
        }, {
            "id": "geojson-editor-point",
            "type": "circle",
            "filter": ["==", "$type", "Point"],
            "paint": {
                "circle-radius": 3,
                "circle-color": "#3bb2d0"
            },
            "source": "geojson-editor-data"
        }]);

        MapComponentViewModel.apply(this, [params]);

        this.deleteFeature = function(feature) {
            if (self.draw) {
                self.draw.delete(feature.id);
                self.updateTiles();
            }
        };

        this.editFeature = function(feature) {
            if (self.draw) {
                self.draw.changeMode('simple_select', {
                    featureIds: [feature.id]
                });
                self.selectedFeatureIds([feature.id]);
                _.each(self.featureLookup, function(value) {
                    value.selectedTool(null);
                });
            }
        };

        this.updateLayers = function(layers) {
            var map = self.map();
            var style = map.getStyle();
            style.layers = self.draw ? layers.concat(self.draw.options.styles) : layers;
            map.setStyle(style);
        };

        this.fitFeatures = function(features) {
            var map = self.map();
            var bounds = geojsonExtent({
                type: 'FeatureCollection',
                features: features
            });
            var camera = map.cameraForBounds(bounds, { padding: padding });
            map.jumpTo(camera);
        };

        this.editGeoJSON = function(features, nodeId) {
            var geoJSONString = JSON.stringify({
                type: 'FeatureCollection',
                features: features
            }, null, '   ');
            this.geoJSONString(geoJSONString);
            self.newNodeId = nodeId;
        };
        this.geoJSONString.subscribe(function(geoJSONString) {
            var map = self.map();
            if (geoJSONString === undefined) {
                setupDraw(map);
            } else if (self.draw) {
                map.removeControl(self.draw);
                self.draw = undefined;
                self.selectedFeatureIds([]);
            }
            self.setSelectLayersVisibility(false);
        });
        this.geoJSONErrors = ko.pureComputed(function() {
            var geoJSONString = self.geoJSONString();
            var hint = geojsonhint.hint(geoJSONString);
            var errors = [];
            hint.forEach(function(item) {
                if (item.level !== 'message') {
                    errors.push(item);
                }
            });
            return errors;
        }).extend({ rateLimit: 50 });
        var geoJSONLayerData = ko.pureComputed(function() {
            var geoJSONString = self.geoJSONString();
            var geoJSONErrors = self.geoJSONErrors();
            if (geoJSONErrors.length === 0) return JSON.parse(geoJSONString);
            else return {
                type: 'FeatureCollection',
                features: []
            };
        }).extend({ rateLimit: 100 });
        geoJSONLayerData.subscribe(function(data) {
            var map = self.map();
            map.getSource('geojson-editor-data').setData(data);
        });
        this.updateGeoJSON = function() {
            if (self.geoJSONErrors().length === 0) {
                var geoJSON = JSON.parse(this.geoJSONString());
                geoJSON.features.forEach(function(feature) {
                    if (!feature.id) feature.id = uuid.generate();
                    if (!feature.properties) feature.properties = {};
                    feature.properties.nodeId = self.newNodeId;
                });
                if (ko.isObservable(self.tile.data[self.newNodeId])) {
                    self.tile.data[self.newNodeId](geoJSON);
                } else {
                    self.tile.data[self.newNodeId].features(geoJSON.features);
                }
                self.geoJSONString(undefined);
            }
        };

        var setupDraw = function(map) {
            var modes = MapboxDraw.modes;
            modes.static = {
                onSetup: function() {
                    this.setActionableState();
                    return {};
                },
                toDisplayFeatures: function(state, geojson, display) {
                    display(geojson);
                }
            };
            self.draw = new MapboxDraw({
                displayControlsDefault: false,
                modes: modes
            });
            map.addControl(self.draw);
            self.draw.set({
                type: 'FeatureCollection',
                features: getDrawFeatures()
            });
            map.on('draw.create', function(e) {
                e.features.forEach(function(feature) {
                    self.draw.setFeatureProperty(feature.id, 'nodeId', self.newNodeId);
                });
                self.updateTiles();
            });
            map.on('draw.update', self.updateTiles);
            map.on('draw.delete', self.updateTiles);
            map.on('draw.modechange', function() {
                self.updateTiles();
                self.setSelectLayersVisibility(false);
            });
            map.on('draw.selectionchange', function(e) {
                self.selectedFeatureIds(e.features.map(function(feature) {
                    return feature.id;
                }));
                if (e.features.length > 0) {
                    _.each(self.featureLookup, function(value) {
                        value.selectedTool(null);
                    });
                }
                self.setSelectLayersVisibility(false);
            });

            self.form.on('tile-reset', function() {
                self.draw.set({
                    type: 'FeatureCollection',
                    features: getDrawFeatures()
                });
                _.each(self.featureLookup, function(value) {
                    if (value.selectedTool()) value.selectedTool('');
                });
            });
        };

        this.map.subscribe(setupDraw);

        if (!params.additionalDrawOptions) {
            params.additionalDrawOptions = [];
        }

        self.card.widgets().forEach(function(widget) {
            if (widget.config.geometryTypes) {
                widget.drawTools = ko.pureComputed(function() {
                    var options = [{
                        value: '',
                        text: ''
                    }];
                    options = options.concat(widget.config.geometryTypes().map(function(type) {
                        var option = {};
                        switch (type.id) {
                        case 'Point':
                            option.value = 'draw_point';
                            option.text = 'Add point';
                            break;
                        case 'Line':
                            option.value = 'draw_line_string';
                            option.text = 'Add line';
                            break;
                        case 'Polygon':
                            option.value = 'draw_polygon';
                            option.text = 'Add polygon';
                            break;
                        }
                        return option;
                    }));
                    if (selectSource) {
                        options.push({
                            value: "select_feature",
                            text: self.selectText
                        });
                    }
                    options = options.concat(params.additionalDrawOptions);
                    return options;
                });
            }
        });

        this.isFeatureClickable = function(feature) {
            var tool = self.selectedTool();
            if (tool && tool !== 'select_feature') return false;
            return feature.properties.resourceinstanceid;
        };

        this.popupTemplate = popupTemplate;

        self.isSelectable = function(feature) {
            var selectLayerIds = selectFeatureLayers.map(function(layer) {
                return layer.id;
            });
            return selectLayerIds.indexOf(feature.layer.id) >= 0;
        };

        self.selectFeature = function(feature) {
            var newFeature = {
                "id": uuid.generate(),
                "type": "Feature",
                "properties": {
                    "nodeId": self.newNodeId
                },
                "geometry": JSON.parse(feature.properties.geojson)
            };
            self.draw.add(newFeature);
            self.updateTiles();
            self.popup.remove();
            self.editFeature(newFeature);
        };
    };
    ko.components.register('map-card', {
        viewModel: viewModel,
        template: {
            require: 'text!templates/views/components/cards/map.htm'
        }
    });
    return viewModel;
});
<|endoftext|>