<commit_before>/*jslint browser:true, plusplus:true, vars:true */
/*jshint browser:true, jquery:true */
/*jshint -W040:false */ /* to don't alert on "this" in triggerLoad and triggerError */

(function ($, window, document, undefined) {
    'use strict';

    // options
    var lazyLoadXT = 'lazyLoadXT',
        options = {
            autoInit: true,
            autoLoad: false,
            selector: 'img',
            classNojs: 'lazy',
            blankImage: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
            throttle: 99,
            loadEvent: 'pageshow', // check AJAX-loaded content in jQueryMobile
            updateEvent: 'load orientationchange resize scroll touchmove', // page-modified events
            forceEvent: '', // force loading of all elements
            oninit: null, // init handler
            onshow: null, // start loading handler
            onload: null, // load success handler
            onerror: null // error handler
        },
        elementOptions = {
            srcAttr: 'data-src',
            edgeX: 0,
            edgeY: 0,
            visibleOnly: true
        },
        $window = $(window),
        $isFunction = $.isFunction,
        $extend = $.extend,
        docElement = document.documentElement || document.body,
    //  autoload all images in Opera Mini and some mobile browsers without scroll event or getBoundingClientRect()
        autoLoad = (window.onscroll === undefined || !!window.operamini || !docElement.getBoundingClientRect),
        dataLazied = 'lazied',
        load_error = 'load error',
        elements = [],
        $data = $.data || function (el, name) {
            return $(el).data(name);
        },
        topLazy = 0,
    /*
     waitingMode=0 : no setTimeout
     waitingMode=1 : setTimeout, no deferred events
     waitingMode=2 : setTimeout, deferred events
     */
        waitingMode = 0;

    $[lazyLoadXT] = $extend(options, elementOptions, $[lazyLoadXT]);

    /**
     * Return def if value is undefined, otherwise return value
     * @param {*} value
     * @param {*} def
     * @returns *
     */
    function getOrDef(value, def) {
        return value === undefined ? def : value;
    }

    /**
     * Add new elements to lazy-load list:
     * $(elements).lazyLoadXT() or $(window).lazyLoadXT()
     *
     * @param {object} [overrides] loading of all elements
     */
    $.fn[lazyLoadXT] = function (overrides) {
        overrides = overrides || {};

        var elementOptionsOverrides = {},
            blankImage = getOrDef(overrides.blankImage, options.blankImage),
            classNojs = getOrDef(overrides.classNojs, options.classNojs),
            checkDuplicates = getOrDef(overrides.checkDuplicates, true),
            prop;

        for (prop in elementOptions) {
            elementOptionsOverrides[prop] = getOrDef(overrides[prop], options[prop]);
        }

        return this.each(function () {
            if (this === window) {
                $(options.selector).lazyLoadXT(overrides);
            } else {
                // prevent duplicates
                if (checkDuplicates && $data(this, dataLazied)) {
                    return;
                }

                var $el = $(this);

                $el
                    .data(dataLazied, 1)
                    .removeClass(classNojs);

                if (blankImage && $el[0].tagName === 'IMG' && !this.src) {
                    this.src = blankImage;
                }

                // clone elementOptionsOverrides object
                $el[lazyLoadXT] = $extend({}, elementOptionsOverrides);

                triggerEvent('init', $el);

                elements.unshift($el); // push it in the first position as we iterate elements in reverse order
            }
        });
    };

    /**
     * Process function/object event handler
     * @param {string} event suffix
     * @param {jQuery} $el
     */
    function triggerEvent(event, $el) {
        $el.trigger('lazy' + event, [$el]);

        var handler = options['on' + event];
        if (handler) {
            if ($isFunction(handler)) {
                handler.call($el[0]);
            } else {
                $el
                    .addClass(handler.addClass || '')
                    .removeClass(handler.removeClass || '');
            }
        }

        // queue next check as images may be resized after loading of actual file
        queueCheckLazyElements();
    }

    /**
     * Trigger onload/onerror handler
     * @param {Event} e
     */
    function triggerLoadOrError(e) {
        triggerEvent(e.type, $(this).off(load_error, triggerLoadOrError));
    }

    /**
     * Load visible elements
     * @param {bool} [force] loading of all elements
     */
    function checkLazyElements(force) {
        if (!elements.length) {
            return;
        }

        force = force || autoLoad || options.autoLoad;

        topLazy = Infinity;

        var viewportTop = $window.scrollTop(),
            viewportHeight = window.innerHeight || $window.height(),
            viewportWidth = window.innerWidth || $window.width(),
            i;

        for (i = elements.length - 1; i >= 0; i--) {
            var $el = elements[i],
                el = $el[0],
                objData = $el[lazyLoadXT],

            // remove items that are not in DOM
            if (!$.contains(docElement, el)) {
                elements.splice(i, 1);
            } else if (force || !objData.visibleOnly || el.offsetWidth > 0 || el.offsetHeight > 0) {

                var visible = force,
                    topEdge;

                if (!visible) {
                    var elPos = el.getBoundingClientRect(),
                        edgeX = objData.edgeX,
                        edgeY = objData.edgeY;

                    topEdge = (elPos.top + viewportTop - edgeY) - viewportHeight;
                    visible = (topEdge <= viewportTop && elPos.bottom > -edgeY &&
                               elPos.left <= viewportWidth + edgeX && elPos.right > -edgeX);
                }

                if (visible) {

                    triggerEvent('show', $el);

                    var srcAttr = objData.srcAttr,
                        src = $isFunction(srcAttr) ? srcAttr($el) : el.getAttribute(srcAttr);
                    if (src) {
                        $el.on(load_error, triggerLoadOrError);
                        el.src = src;
                    }

                    elements.splice(i, 1);
                } else {
                    if (topEdge < topLazy) {
                        topLazy = topEdge;
                    }
                }
            }
        }

        if (!elements.length) {
            $(document).trigger('lazyloadall');
        }
    }

    /**
     * Run check of lazy elements after timeout
     */
    function timeoutLazyElements() {
        if (waitingMode > 1) {
            waitingMode = 1;
            checkLazyElements();
            setTimeout(timeoutLazyElements, options.throttle);
        } else {
            waitingMode = 0;
        }
    }

    /**
     * Queue check of lazy elements because of event e
     * @param {Event} [e]
     */
    function queueCheckLazyElements(e) {
        if (!elements.length) {
            return;
        }

        // fast check for scroll event without new visible elements
        if (e && e.type === 'scroll') {
            if (topLazy >= $window.scrollTop()) {
                return;
            }
        }

        if (!waitingMode) {
            setTimeout(timeoutLazyElements, 0);
        }

        waitingMode = 2;
    }

    /**
     * Initialize list of hidden elements
     */
    function initLazyElements() {
        $window.lazyLoadXT();
        queueCheckLazyElements();
    }

    /**
     * Loading of all elements
     */
    function forceLoadAll() {
        checkLazyElements(true);
    }

    /**
     * Initialization
     */
    $(document).ready(function () {
        $window
            .on(options.loadEvent, initLazyElements)
            .on(options.updateEvent, queueCheckLazyElements)
            .on(options.forceEvent, forceLoadAll);

        if (options.autoInit) {
            initLazyElements(); // standard initialization
        }
    });

})(window.jQuery || window.Zepto, window, document);
<commit_msg>change selector to img[data-src] (to don't affect usual images like tracking images, etc.)<commit_after>/*jslint browser:true, plusplus:true, vars:true */
/*jshint browser:true, jquery:true */
/*jshint -W040:false */ /* to don't alert on "this" in triggerLoad and triggerError */

(function ($, window, document, undefined) {
    'use strict';

    // options
    var lazyLoadXT = 'lazyLoadXT',
        options = {
            autoInit: true,
            autoLoad: false,
            selector: 'img[data-src]',
            classNojs: 'lazy',
            blankImage: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
            throttle: 99,
            loadEvent: 'pageshow', // check AJAX-loaded content in jQueryMobile
            updateEvent: 'load orientationchange resize scroll touchmove', // page-modified events
            forceEvent: '', // force loading of all elements
            oninit: null, // init handler
            onshow: null, // start loading handler
            onload: null, // load success handler
            onerror: null // error handler
        },
        elementOptions = {
            srcAttr: 'data-src',
            edgeX: 0,
            edgeY: 0,
            visibleOnly: true
        },
        $window = $(window),
        $isFunction = $.isFunction,
        $extend = $.extend,
        docElement = document.documentElement || document.body,
    //  autoload all images in Opera Mini and some mobile browsers without scroll event or getBoundingClientRect()
        autoLoad = (window.onscroll === undefined || !!window.operamini || !docElement.getBoundingClientRect),
        dataLazied = 'lazied',
        load_error = 'load error',
        elements = [],
        $data = $.data || function (el, name) {
            return $(el).data(name);
        },
        topLazy = 0,
    /*
     waitingMode=0 : no setTimeout
     waitingMode=1 : setTimeout, no deferred events
     waitingMode=2 : setTimeout, deferred events
     */
        waitingMode = 0;

    $[lazyLoadXT] = $extend(options, elementOptions, $[lazyLoadXT]);

    /**
     * Return def if value is undefined, otherwise return value
     * @param {*} value
     * @param {*} def
     * @returns *
     */
    function getOrDef(value, def) {
        return value === undefined ? def : value;
    }

    /**
     * Add new elements to lazy-load list:
     * $(elements).lazyLoadXT() or $(window).lazyLoadXT()
     *
     * @param {object} [overrides] loading of all elements
     */
    $.fn[lazyLoadXT] = function (overrides) {
        overrides = overrides || {};

        var elementOptionsOverrides = {},
            blankImage = getOrDef(overrides.blankImage, options.blankImage),
            classNojs = getOrDef(overrides.classNojs, options.classNojs),
            checkDuplicates = getOrDef(overrides.checkDuplicates, true),
            prop;

        for (prop in elementOptions) {
            elementOptionsOverrides[prop] = getOrDef(overrides[prop], options[prop]);
        }

        return this.each(function () {
            if (this === window) {
                $(options.selector).lazyLoadXT(overrides);
            } else {
                // prevent duplicates
                if (checkDuplicates && $data(this, dataLazied)) {
                    return;
                }

                var $el = $(this);

                $el
                    .data(dataLazied, 1)
                    .removeClass(classNojs);

                if (blankImage && $el[0].tagName === 'IMG' && !this.src) {
                    this.src = blankImage;
                }

                // clone elementOptionsOverrides object
                $el[lazyLoadXT] = $extend({}, elementOptionsOverrides);

                triggerEvent('init', $el);

                elements.unshift($el); // push it in the first position as we iterate elements in reverse order
            }
        });
    };

    /**
     * Process function/object event handler
     * @param {string} event suffix
     * @param {jQuery} $el
     */
    function triggerEvent(event, $el) {
        $el.trigger('lazy' + event, [$el]);

        var handler = options['on' + event];
        if (handler) {
            if ($isFunction(handler)) {
                handler.call($el[0]);
            } else {
                $el
                    .addClass(handler.addClass || '')
                    .removeClass(handler.removeClass || '');
            }
        }

        // queue next check as images may be resized after loading of actual file
        queueCheckLazyElements();
    }

    /**
     * Trigger onload/onerror handler
     * @param {Event} e
     */
    function triggerLoadOrError(e) {
        triggerEvent(e.type, $(this).off(load_error, triggerLoadOrError));
    }

    /**
     * Load visible elements
     * @param {bool} [force] loading of all elements
     */
    function checkLazyElements(force) {
        if (!elements.length) {
            return;
        }

        force = force || autoLoad || options.autoLoad;

        topLazy = Infinity;

        var viewportTop = $window.scrollTop(),
            viewportHeight = window.innerHeight || $window.height(),
            viewportWidth = window.innerWidth || $window.width(),
            i;

        for (i = elements.length - 1; i >= 0; i--) {
            var $el = elements[i],
                el = $el[0],
                objData = $el[lazyLoadXT],

            // remove items that are not in DOM
            if (!$.contains(docElement, el)) {
                elements.splice(i, 1);
            } else if (force || !objData.visibleOnly || el.offsetWidth > 0 || el.offsetHeight > 0) {

                var visible = force,
                    topEdge;

                if (!visible) {
                    var elPos = el.getBoundingClientRect(),
                        edgeX = objData.edgeX,
                        edgeY = objData.edgeY;

                    topEdge = (elPos.top + viewportTop - edgeY) - viewportHeight;
                    visible = (topEdge <= viewportTop && elPos.bottom > -edgeY &&
                               elPos.left <= viewportWidth + edgeX && elPos.right > -edgeX);
                }

                if (visible) {

                    triggerEvent('show', $el);

                    var srcAttr = objData.srcAttr,
                        src = $isFunction(srcAttr) ? srcAttr($el) : el.getAttribute(srcAttr);
                    if (src) {
                        $el.on(load_error, triggerLoadOrError);
                        el.src = src;
                    }

                    elements.splice(i, 1);
                } else {
                    if (topEdge < topLazy) {
                        topLazy = topEdge;
                    }
                }
            }
        }

        if (!elements.length) {
            $(document).trigger('lazyloadall');
        }
    }

    /**
     * Run check of lazy elements after timeout
     */
    function timeoutLazyElements() {
        if (waitingMode > 1) {
            waitingMode = 1;
            checkLazyElements();
            setTimeout(timeoutLazyElements, options.throttle);
        } else {
            waitingMode = 0;
        }
    }

    /**
     * Queue check of lazy elements because of event e
     * @param {Event} [e]
     */
    function queueCheckLazyElements(e) {
        if (!elements.length) {
            return;
        }

        // fast check for scroll event without new visible elements
        if (e && e.type === 'scroll') {
            if (topLazy >= $window.scrollTop()) {
                return;
            }
        }

        if (!waitingMode) {
            setTimeout(timeoutLazyElements, 0);
        }

        waitingMode = 2;
    }

    /**
     * Initialize list of hidden elements
     */
    function initLazyElements() {
        $window.lazyLoadXT();
        queueCheckLazyElements();
    }

    /**
     * Loading of all elements
     */
    function forceLoadAll() {
        checkLazyElements(true);
    }

    /**
     * Initialization
     */
    $(document).ready(function () {
        $window
            .on(options.loadEvent, initLazyElements)
            .on(options.updateEvent, queueCheckLazyElements)
            .on(options.forceEvent, forceLoadAll);

        if (options.autoInit) {
            initLazyElements(); // standard initialization
        }
    });

})(window.jQuery || window.Zepto, window, document);
<|endoftext|>


<commit_before>/**
 * The main application controller
 */

'use strict';

var axe = require('axe-logger'),
    Auth = require('./bo/auth'),
    PGP = require('./crypto/pgp'),
    PgpMailer = require('pgpmailer'),
    OAuth = require('./util/oauth'),
    PgpBuilder = require('pgpbuilder'),
    OutboxBO = require('./bo/outbox'),
    mailreader = require('mailreader'),
    ImapClient = require('imap-client'),
    Crypto = require('./crypto/crypto'),
    RestDAO = require('./dao/rest-dao'),
    appConfig = require('./app-config'),
    EmailDAO = require('./dao/email-dao'),
    AdminDao = require('./dao/admin-dao'),
    KeychainDAO = require('./dao/keychain-dao'),
    PublicKeyDAO = require('./dao/publickey-dao'),
    LawnchairDAO = require('./dao/lawnchair-dao'),
    PrivateKeyDAO = require('./dao/privatekey-dao'),
    InvitationDAO = require('./dao/invitation-dao'),
    DeviceStorageDAO = require('./dao/devicestorage-dao'),
    ConnectionDoctor = require('./util/connection-doctor'),
    UpdateHandler = require('./util/update/update-handler'),
    config = appConfig.config,
    str = appConfig.string;

var ctrl = {};

/**
 * Start the application.
 */
ctrl.start = function(options, callback) {
    if (ctrl.started) {
        return callback();
    }

    ctrl.started = true;
    ctrl.onError = options.onError;

    // are we running in a cordova app or in a browser environment?
    if (window.cordova) {
        // wait for 'deviceready' event to make sure plugins are loaded
        axe.debug('Assuming Cordova environment...');
        document.addEventListener("deviceready", onDeviceReady, false);
    } else {
        // No need to wait on events... just start the app
        axe.debug('Assuming Browser environment...');
        onDeviceReady();
    }

    function onDeviceReady() {
        axe.debug('Starting app.');

        ctrl.buildModules();

        // Handle offline and online gracefully
        window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError));
        window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl));

        ctrl._appConfigStore.init('app-config', callback);
    }
};

/**
 * Initialize the dependency tree.
 */
ctrl.buildModules = function() {
    var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth;

    // start the mailreader's worker thread
    mailreader.startWorker(config.workerPath + '/mailreader-parser-worker.min.js');

    // init objects and inject dependencies
    restDao = new RestDAO();
    lawnchairDao = new LawnchairDAO();
    pubkeyDao = new PublicKeyDAO(restDao);
    privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl));
    oauth = new OAuth(new RestDAO('https://www.googleapis.com'));

    crypto = new Crypto();
    ctrl._pgp = pgp = new PGP();
    ctrl._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
    keychain.requestPermissionForKeyUpdate = function(params, callback) {
        var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey;
        message = message.replace('{0}', params.userId);

        ctrl.onError({
            title: str.updatePublicKeyTitle,
            message: message,
            positiveBtnStr: str.updatePublicKeyPosBtn,
            negativeBtnStr: str.updatePublicKeyNegBtn,
            showNegativeBtn: true,
            callback: callback
        });
    };

    ctrl._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO());
    ctrl._auth = auth = new Auth(appConfigStore, oauth, pgp);
    ctrl._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao);
    ctrl._invitationDao = new InvitationDAO(restDao);
    ctrl._pgpbuilder = pgpbuilder = new PgpBuilder();
    ctrl._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
    ctrl._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
    ctrl._updateHandler = new UpdateHandler(appConfigStore, userStorage, auth);
    ctrl._adminDao = new AdminDao(new RestDAO(config.adminUrl));
    ctrl._doctor = new ConnectionDoctor();

    emailDao.onError = ctrl.onError;
};

/**
 * Calls runtime hooks to check if an app update is available.
 */
ctrl.checkForUpdate = function() {
    ctrl._updateHandler.checkForUpdate(ctrl.onError);
};

/**
 * Instanciate the mail email data access object and its dependencies. Login to imap on init.
 */
ctrl.init = function(options, callback) {
    // init user's local database
    ctrl._userStorage.init(options.emailAddress, function(err) {
        if (err) {
            callback(err);
            return;
        }

        // Migrate the databases if necessary
        ctrl._updateHandler.update(onUpdate);
    });

    function onUpdate(err) {
        if (err) {
            callback({
                errMsg: 'Update failed, please reinstall the app.',
                err: err
            });
            return;
        }

        // account information for the email dao
        var account = {
            realname: options.realname,
            emailAddress: options.emailAddress,
            asymKeySize: config.asymKeySize
        };

        // init email dao
        ctrl._emailDao.init({
            account: account
        }, function(err, keypair) {
            if (err) {
                callback(err);
                return;
            }

            callback(null, keypair);
        });
    }
};

/**
 * Check if the user agent is online.
 */
ctrl.isOnline = function() {
    return navigator.onLine;
};

/**
 * Event handler that is called when the user agent goes offline.
 */
ctrl.onDisconnect = function() {
    ctrl._emailDao.onDisconnect();
};

/**
 * Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer.
 */
ctrl.logout = function() {
    // clear app config store
    ctrl._auth.logout(function(err) {
        if (err) {
            ctrl.onError(err);
            return;
        }

        // delete instance of imap-client and pgp-mailer
        ctrl._emailDao.onDisconnect(function(err) {
            if (err) {
                ctrl.onError(err);
                return;
            }

            // navigate to login
            window.location.href = '/';
        });
    });
};

/**
 * Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
 */
ctrl.onConnect = function(callback) {
    if (!ctrl.isOnline() || !ctrl._emailDao || !ctrl._emailDao._account) {
        // prevent connection infinite loop
        callback();
        return;
    }

    ctrl._auth.getCredentials(function(err, credentials) {
        if (err) {
            callback(err);
            return;
        }

        initClients(credentials);
    });

    function initClients(credentials) {
        // add the maximum update batch size for imap folders to the imap configuration
        credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;

        // tls socket worker path for multithreaded tls in non-native tls environments
        credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';

        var pgpMailer = new PgpMailer(credentials.smtp, ctrl._pgpbuilder);
        var imapClient = new ImapClient(credentials.imap);
        imapClient.onError = onConnectionError;
        pgpMailer.onError = onConnectionError;

        // certificate update handling
        imapClient.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'imap', ctrl.onConnect, ctrl.onError);
        pgpMailer.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'smtp', ctrl.onConnect, ctrl.onError);

        // after-setup configuration depending on the provider:
        // gmail does not require you to upload to the sent items folder
        // after successful sending, whereas most other providers do
        ctrl._emailDao.ignoreUploadOnSent = !!(config[ctrl._auth.provider] && config[ctrl._auth.provider].ignoreUploadOnSent);

        // connect to clients
        ctrl._emailDao.onConnect({
            imapClient: imapClient,
            pgpMailer: pgpMailer
        }, callback);
    }

    function onConnectionError(error) {
        axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));

        setTimeout(function() {
            axe.debug('Reconnecting...');
            // re-init client modules on error
            ctrl.onConnect(function(err) {
                if (err) {
                    axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
                    return;
                }

                axe.debug('Reconnect attempt complete.');
            });
        }, config.reconnectInterval);
    }
};

module.exports = ctrl;
<commit_msg>[WO-656] fix logout in chrome app<commit_after>/**
 * The main application controller
 */

'use strict';

var axe = require('axe-logger'),
    Auth = require('./bo/auth'),
    PGP = require('./crypto/pgp'),
    PgpMailer = require('pgpmailer'),
    OAuth = require('./util/oauth'),
    PgpBuilder = require('pgpbuilder'),
    OutboxBO = require('./bo/outbox'),
    mailreader = require('mailreader'),
    ImapClient = require('imap-client'),
    Crypto = require('./crypto/crypto'),
    RestDAO = require('./dao/rest-dao'),
    appConfig = require('./app-config'),
    EmailDAO = require('./dao/email-dao'),
    AdminDao = require('./dao/admin-dao'),
    KeychainDAO = require('./dao/keychain-dao'),
    PublicKeyDAO = require('./dao/publickey-dao'),
    LawnchairDAO = require('./dao/lawnchair-dao'),
    PrivateKeyDAO = require('./dao/privatekey-dao'),
    InvitationDAO = require('./dao/invitation-dao'),
    DeviceStorageDAO = require('./dao/devicestorage-dao'),
    ConnectionDoctor = require('./util/connection-doctor'),
    UpdateHandler = require('./util/update/update-handler'),
    config = appConfig.config,
    str = appConfig.string;

var ctrl = {};

/**
 * Start the application.
 */
ctrl.start = function(options, callback) {
    if (ctrl.started) {
        return callback();
    }

    ctrl.started = true;
    ctrl.onError = options.onError;

    // are we running in a cordova app or in a browser environment?
    if (window.cordova) {
        // wait for 'deviceready' event to make sure plugins are loaded
        axe.debug('Assuming Cordova environment...');
        document.addEventListener("deviceready", onDeviceReady, false);
    } else {
        // No need to wait on events... just start the app
        axe.debug('Assuming Browser environment...');
        onDeviceReady();
    }

    function onDeviceReady() {
        axe.debug('Starting app.');

        ctrl.buildModules();

        // Handle offline and online gracefully
        window.addEventListener('online', ctrl.onConnect.bind(ctrl, ctrl.onError));
        window.addEventListener('offline', ctrl.onDisconnect.bind(ctrl));

        ctrl._appConfigStore.init('app-config', callback);
    }
};

/**
 * Initialize the dependency tree.
 */
ctrl.buildModules = function() {
    var lawnchairDao, restDao, pubkeyDao, privkeyDao, crypto, emailDao, keychain, pgp, userStorage, pgpbuilder, oauth, appConfigStore, auth;

    // start the mailreader's worker thread
    mailreader.startWorker(config.workerPath + '/mailreader-parser-worker.min.js');

    // init objects and inject dependencies
    restDao = new RestDAO();
    lawnchairDao = new LawnchairDAO();
    pubkeyDao = new PublicKeyDAO(restDao);
    privkeyDao = new PrivateKeyDAO(new RestDAO(config.privkeyServerUrl));
    oauth = new OAuth(new RestDAO('https://www.googleapis.com'));

    crypto = new Crypto();
    ctrl._pgp = pgp = new PGP();
    ctrl._keychain = keychain = new KeychainDAO(lawnchairDao, pubkeyDao, privkeyDao, crypto, pgp);
    keychain.requestPermissionForKeyUpdate = function(params, callback) {
        var message = params.newKey ? str.updatePublicKeyMsgNewKey : str.updatePublicKeyMsgRemovedKey;
        message = message.replace('{0}', params.userId);

        ctrl.onError({
            title: str.updatePublicKeyTitle,
            message: message,
            positiveBtnStr: str.updatePublicKeyPosBtn,
            negativeBtnStr: str.updatePublicKeyNegBtn,
            showNegativeBtn: true,
            callback: callback
        });
    };

    ctrl._appConfigStore = appConfigStore = new DeviceStorageDAO(new LawnchairDAO());
    ctrl._auth = auth = new Auth(appConfigStore, oauth, pgp);
    ctrl._userStorage = userStorage = new DeviceStorageDAO(lawnchairDao);
    ctrl._invitationDao = new InvitationDAO(restDao);
    ctrl._pgpbuilder = pgpbuilder = new PgpBuilder();
    ctrl._emailDao = emailDao = new EmailDAO(keychain, pgp, userStorage, pgpbuilder, mailreader);
    ctrl._outboxBo = new OutboxBO(emailDao, keychain, userStorage);
    ctrl._updateHandler = new UpdateHandler(appConfigStore, userStorage, auth);
    ctrl._adminDao = new AdminDao(new RestDAO(config.adminUrl));
    ctrl._doctor = new ConnectionDoctor();

    emailDao.onError = ctrl.onError;
};

/**
 * Calls runtime hooks to check if an app update is available.
 */
ctrl.checkForUpdate = function() {
    ctrl._updateHandler.checkForUpdate(ctrl.onError);
};

/**
 * Instanciate the mail email data access object and its dependencies. Login to imap on init.
 */
ctrl.init = function(options, callback) {
    // init user's local database
    ctrl._userStorage.init(options.emailAddress, function(err) {
        if (err) {
            callback(err);
            return;
        }

        // Migrate the databases if necessary
        ctrl._updateHandler.update(onUpdate);
    });

    function onUpdate(err) {
        if (err) {
            callback({
                errMsg: 'Update failed, please reinstall the app.',
                err: err
            });
            return;
        }

        // account information for the email dao
        var account = {
            realname: options.realname,
            emailAddress: options.emailAddress,
            asymKeySize: config.asymKeySize
        };

        // init email dao
        ctrl._emailDao.init({
            account: account
        }, function(err, keypair) {
            if (err) {
                callback(err);
                return;
            }

            callback(null, keypair);
        });
    }
};

/**
 * Check if the user agent is online.
 */
ctrl.isOnline = function() {
    return navigator.onLine;
};

/**
 * Event handler that is called when the user agent goes offline.
 */
ctrl.onDisconnect = function() {
    ctrl._emailDao.onDisconnect();
};

/**
 * Log the current user out by clear the app config store and deleting instances of imap-client and pgp-mailer.
 */
ctrl.logout = function() {
    // clear app config store
    ctrl._auth.logout(function(err) {
        if (err) {
            ctrl.onError(err);
            return;
        }

        // delete instance of imap-client and pgp-mailer
        ctrl._emailDao.onDisconnect(function(err) {
            if (err) {
                ctrl.onError(err);
                return;
            }

            if (typeof window.chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
                // reload chrome app
                chrome.runtime.reload();
            } else {
                // navigate to login
                window.location.href = '/';
            }
        });
    });
};

/**
 * Event that is called when the user agent goes online. This create new instances of the imap-client and pgp-mailer and connects to the mail server.
 */
ctrl.onConnect = function(callback) {
    if (!ctrl.isOnline() || !ctrl._emailDao || !ctrl._emailDao._account) {
        // prevent connection infinite loop
        callback();
        return;
    }

    ctrl._auth.getCredentials(function(err, credentials) {
        if (err) {
            callback(err);
            return;
        }

        initClients(credentials);
    });

    function initClients(credentials) {
        // add the maximum update batch size for imap folders to the imap configuration
        credentials.imap.maxUpdateSize = config.imapUpdateBatchSize;

        // tls socket worker path for multithreaded tls in non-native tls environments
        credentials.imap.tlsWorkerPath = credentials.smtp.tlsWorkerPath = config.workerPath + '/tcp-socket-tls-worker.min.js';

        var pgpMailer = new PgpMailer(credentials.smtp, ctrl._pgpbuilder);
        var imapClient = new ImapClient(credentials.imap);
        imapClient.onError = onConnectionError;
        pgpMailer.onError = onConnectionError;

        // certificate update handling
        imapClient.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'imap', ctrl.onConnect, ctrl.onError);
        pgpMailer.onCert = ctrl._auth.handleCertificateUpdate.bind(ctrl._auth, 'smtp', ctrl.onConnect, ctrl.onError);

        // after-setup configuration depending on the provider:
        // gmail does not require you to upload to the sent items folder
        // after successful sending, whereas most other providers do
        ctrl._emailDao.ignoreUploadOnSent = !!(config[ctrl._auth.provider] && config[ctrl._auth.provider].ignoreUploadOnSent);

        // connect to clients
        ctrl._emailDao.onConnect({
            imapClient: imapClient,
            pgpMailer: pgpMailer
        }, callback);
    }

    function onConnectionError(error) {
        axe.debug('Connection error. Attempting reconnect in ' + config.reconnectInterval + ' ms. Error: ' + (error.errMsg || error.message) + (error.stack ? ('\n' + error.stack) : ''));

        setTimeout(function() {
            axe.debug('Reconnecting...');
            // re-init client modules on error
            ctrl.onConnect(function(err) {
                if (err) {
                    axe.error('Reconnect attempt failed! ' + (err.errMsg || err.message) + (err.stack ? ('\n' + err.stack) : ''));
                    return;
                }

                axe.debug('Reconnect attempt complete.');
            });
        }, config.reconnectInterval);
    }
};

module.exports = ctrl;<|endoftext|>


<commit_before>// REST API for doors.

const db = require('../lib/db');
const crypto = require('crypto');
const error = require('../lib/errors');
const helpers = require('../lib/helpers');
const expressWs = require('express-ws');
const DOOR_SOCKETS = {};

module.exports = function(app) {
	var wsApp = expressWs(app);
	app.   get("/doors", index);
	app.  post("/doors", create);
	app.   get("/doors/:id", read);
	app. patch("/doors/:id", update);
	app.delete("/doors/:id", del_door);
	app.   get("/doors/:id/logs", logs);
	app.  post("/doors/:id/open", open);
	app.    ws("/doors/:id/connect", connect);
	app.  post("/doors/:id/permit/:user_id", permit);
	app.delete("/doors/:id/permit/:user_id", deny);
}

async function index(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (user.admin) {
		var doors = await db.all("SELECT * FROM doors");
	} else {
		var doors = await db.all(`
			SELECT doors.* FROM doors
			INNER JOIN permissions on doors.id = permissions.door_id
			WHERE permissions.user_id = ?`,
			user.id);
	}

	response.writeHead(200);
	const door_l = [];
	for (const door of doors) {
		door_l.push({
			id: door.id,
			name: door.name,
			token: user.admin ? door.token : null,
			available: DOOR_SOCKETS[door.id] && DOOR_SOCKETS[door.id].readyState == 1,
		});
	}
	response.write(JSON.stringify(door_l));
	response.end();
}

async function create(request, response) {
	if (!request.body.name) {
		response.writeHead(400);
		response.write("name is required.");
		return response.end();
	}

	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		response.end();
		return;
	}

	const token = crypto.createHash("sha256")
		.update(Math.random().toString()).digest('hex');
	try {
		var r = await db.run("INSERT INTO doors (name, token) VALUES (?,?)",
			request.body.name, token);
	} catch(e) {
		response.writeHead(400);
		response.write("Door name already used");
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: r.stmt.lastID,
		name: request.body.name,
		token: token,
	}));
	response.end();
}

async function read(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (user.admin) {
		var door = await db.get(
			"SELECT * FROM doors WHERE id = ?", request.params.id);
	} else {
		var door = await db.get(`
			SELECT * FROM doors
			INNER JOIN permissions on doors.id = permissions.door_id
			WHERE permissions.user_id = ? AND doors.id = ?`,
			user.id, request.params.id);
	}

	if (!door) {
		response.writeHead(404);
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: door.id,
		name: door.name,
		token: user.admin ? door.token : null,
	}));
	response.end();
}

async function update(request, response) {
	if (!request.body.name) {
		response.writeHead(400);
		response.write("name is required.");
		return response.end();
	}

	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	try {
		await db.run("UPDATE doors SET name = ? WHERE id = ?",
			request.body.name, request.params.id);
	} catch(e) {
		response.writeHead(400);
		response.write("DB update error.");
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: request.params.id,
		name: request.body.name,
	}));
	response.end();
}

async function del_door(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}
	const r = await db.run("DELETE FROM doors WHERE id = ?", request.params.id);
	if (!r.stmt.changes) {
		response.writeHead(404);
	} else {
		response.writeHead(200);
	}
	response.end();
}

async function logs(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}
	try {
		var page = parseInt(request.params.page||1)-1;
	} catch(e) {
		response.writeHead(400);
		response.write("page must be an int");
		return response.end();
	}
	const logs = await db.all(`
		SELECT entry_logs.*, users.username FROM entry_logs
		INNER JOIN users on entry_logs.user_id = users.id
		WHERE door_id = ? ORDER BY entry_logs.id DESC LIMIT ? OFFSET ?`,
		request.params.id, 50, page*50);

	response.writeHead(200);
	response.write(JSON.stringify(logs));
	response.end();
}

async function open(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		if (!user.pw_salt) {
			response.writeHead(422);
			response.write("Your password has been set by an admin and requires reset.");
			return response.end();
		}

		const perm = await db.get(`
			SELECT * FROM permissions
			WHERE door_id = ? AND user_id = ?`,
			request.params.id, user.id);

		if (!perm) {
			response.writeHead(403);
			response.write("You dont have permissions to open this door.");
			return response.end();
		}
	}

	const method = "web:"+request.connection.remoteAddress;

	//TODO: check constraints
	try {
		// open the door
		DOOR_SOCKETS[request.params.id].send('open');
	} catch(e) {
		console.warn("ERROR: could not open door:", e);
		response.writeHead(504);
		response.write("door could not be opened.");
		return response.end();
	}

	await db.run("INSERT INTO entry_logs (user_id, door_id, method) VALUES (?,?,?)",
		request.params.id, user.id, method);

	response.writeHead(200);
	response.end();
}

async function connect(ws, request, next) {
	if (!request.headers.authorization) {
		return ws.close(1007, "no token");
	}
	const door = await db.get("SELECT * FROM doors WHERE id = ? AND token = ?",
		request.params.id, request.headers.authorization);

	if (!door) {
		return ws.close(1007, "bad token");
	}

	DOOR_SOCKETS[request.params.id] = ws;
}

async function permit(request, response) {
	const user = await helpers.check_cookie(request, response)
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	try {
		await db.run("INSERT INTO permissions (door_id, user_id) VALUES (?,?)",
			request.params.id, request.params.user_id);
	} catch(e) {
		response.writeHead(409);
		response.write("Door already permits user.");
		return response.end();
	}

	response.writeHead(200);
	response.end();
}

async function deny(request, response) {
	const user = await helpers.check_cookie(request, response)
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	const r = await db.run(
		"DELETE FROM permissions WHERE door_id = ? AND user_id = ?",
		request.params.id, request.params.user_id);

	if (!r.stmt.changes) {
		response.writeHead(404);
		response.write("Door doesn't permit user.");
	} else {
		response.writeHead(200);
	}

	response.end();
}

<commit_msg>Fixed logs not showing up<commit_after>// REST API for doors.

const db = require('../lib/db');
const crypto = require('crypto');
const error = require('../lib/errors');
const helpers = require('../lib/helpers');
const expressWs = require('express-ws');
const DOOR_SOCKETS = {};

module.exports = function(app) {
	var wsApp = expressWs(app);
	app.   get("/doors", index);
	app.  post("/doors", create);
	app.   get("/doors/:id", read);
	app. patch("/doors/:id", update);
	app.delete("/doors/:id", del_door);
	app.   get("/doors/:id/logs", logs);
	app.  post("/doors/:id/open", open);
	app.    ws("/doors/:id/connect", connect);
	app.  post("/doors/:id/permit/:user_id", permit);
	app.delete("/doors/:id/permit/:user_id", deny);
}

async function index(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (user.admin) {
		var doors = await db.all("SELECT * FROM doors");
	} else {
		var doors = await db.all(`
			SELECT doors.* FROM doors
			INNER JOIN permissions on doors.id = permissions.door_id
			WHERE permissions.user_id = ?`,
			user.id);
	}

	response.writeHead(200);
	const door_l = [];
	for (const door of doors) {
		door_l.push({
			id: door.id,
			name: door.name,
			token: user.admin ? door.token : null,
			available: DOOR_SOCKETS[door.id] && DOOR_SOCKETS[door.id].readyState == 1,
		});
	}
	response.write(JSON.stringify(door_l));
	response.end();
}

async function create(request, response) {
	if (!request.body.name) {
		response.writeHead(400);
		response.write("name is required.");
		return response.end();
	}

	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		response.end();
		return;
	}

	const token = crypto.createHash("sha256")
		.update(Math.random().toString()).digest('hex');
	try {
		var r = await db.run("INSERT INTO doors (name, token) VALUES (?,?)",
			request.body.name, token);
	} catch(e) {
		response.writeHead(400);
		response.write("Door name already used");
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: r.stmt.lastID,
		name: request.body.name,
		token: token,
	}));
	response.end();
}

async function read(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (user.admin) {
		var door = await db.get(
			"SELECT * FROM doors WHERE id = ?", request.params.id);
	} else {
		var door = await db.get(`
			SELECT * FROM doors
			INNER JOIN permissions on doors.id = permissions.door_id
			WHERE permissions.user_id = ? AND doors.id = ?`,
			user.id, request.params.id);
	}

	if (!door) {
		response.writeHead(404);
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: door.id,
		name: door.name,
		token: user.admin ? door.token : null,
	}));
	response.end();
}

async function update(request, response) {
	if (!request.body.name) {
		response.writeHead(400);
		response.write("name is required.");
		return response.end();
	}

	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	try {
		await db.run("UPDATE doors SET name = ? WHERE id = ?",
			request.body.name, request.params.id);
	} catch(e) {
		response.writeHead(400);
		response.write("DB update error.");
		return response.end();
	}

	response.writeHead(200);
	response.write(JSON.stringify({
		id: request.params.id,
		name: request.body.name,
	}));
	response.end();
}

async function del_door(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}
	const r = await db.run("DELETE FROM doors WHERE id = ?", request.params.id);
	if (!r.stmt.changes) {
		response.writeHead(404);
	} else {
		response.writeHead(200);
	}
	response.end();
}

async function logs(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}
	try {
		var page = parseInt(request.params.page||1)-1;
	} catch(e) {
		response.writeHead(400);
		response.write("page must be an int");
		return response.end();
	}
	const logs = await db.all(`
		SELECT entry_logs.*, users.username FROM entry_logs
		INNER JOIN users on entry_logs.user_id = users.id
		WHERE door_id = ? ORDER BY entry_logs.id DESC LIMIT ? OFFSET ?`,
		request.params.id, 50, page*50);

	response.writeHead(200);
	response.write(JSON.stringify(logs));
	response.end();
}

async function open(request, response) {
	const user = await helpers.check_cookie(request, response);
	if (!user.admin) {
		if (!user.pw_salt) {
			response.writeHead(422);
			response.write("Your password has been set by an admin and requires reset.");
			return response.end();
		}

		const perm = await db.get(`
			SELECT * FROM permissions
			WHERE door_id = ? AND user_id = ?`,
			request.params.id, user.id);

		if (!perm) {
			response.writeHead(403);
			response.write("You dont have permissions to open this door.");
			return response.end();
		}
	}

	const method = "web:"+request.connection.remoteAddress;

	//TODO: check constraints
	try {
		// open the door
		DOOR_SOCKETS[request.params.id].send('open');
	} catch(e) {
		console.warn("ERROR: could not open door:", e);
		response.writeHead(504);
		response.write("door could not be opened.");
		return response.end();
	}

	await db.run("INSERT INTO entry_logs (user_id, door_id, method) VALUES (?,?,?)",
		user.id, request.params.id, method);

	response.writeHead(200);
	response.end();
}

async function connect(ws, request, next) {
	if (!request.headers.authorization) {
		return ws.close(1007, "no token");
	}
	const door = await db.get("SELECT * FROM doors WHERE id = ? AND token = ?",
		request.params.id, request.headers.authorization);

	if (!door) {
		return ws.close(1007, "bad token");
	}

	DOOR_SOCKETS[request.params.id] = ws;
}

async function permit(request, response) {
	const user = await helpers.check_cookie(request, response)
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	try {
		await db.run("INSERT INTO permissions (door_id, user_id) VALUES (?,?)",
			request.params.id, request.params.user_id);
	} catch(e) {
		response.writeHead(409);
		response.write("Door already permits user.");
		return response.end();
	}

	response.writeHead(200);
	response.end();
}

async function deny(request, response) {
	const user = await helpers.check_cookie(request, response)
	if (!user.admin) {
		response.writeHead(403);
		response.write("Must be admin");
		return response.end();
	}

	const r = await db.run(
		"DELETE FROM permissions WHERE door_id = ? AND user_id = ?",
		request.params.id, request.params.user_id);

	if (!r.stmt.changes) {
		response.writeHead(404);
		response.write("Door doesn't permit user.");
	} else {
		response.writeHead(200);
	}

	response.end();
}

<|endoftext|>


<commit_before>(function() {
  var firstRun = !window.polis;
  window.polis = window.polis || {};

  function getConfig(d) {
     return {
         conversation_id: d.getAttribute("data-conversation_id"),
         height: d.getAttribute("data-height"),
         demo: d.getAttribute("data-demo")
     };
  }

  function createPolisIframe(parent, o) {
    var iframe = document.createElement("iframe");
    var path = [];
    if (o.demo) {
      path.push("demo");
    }
    path.push(o.conversation_id);
    iframe.src = "https://embed.pol.is/"+ path.join("/");
    iframe.width = "100%"; // may be constrained by parent div
    iframe.height = o.height || 930;
    iframe.style.border = "1px solid #ccc";
    iframe.style.borderRadius = "4px";
    parent.appendChild(iframe);
  }

  function cookiesEnabledAtTopLevel() {
    // create a temporary cookie
    var soon = new Date(Date.now() + 1000).toUTCString();
    var teststring = "_polistest_cookiesenabled";
    document.cookie = teststring + "=1; expires=" + soon;
    // see if it worked
    var cookieEnabled = document.cookie.indexOf(teststring) != -1;
    // clear the cookie
    document.cookie = teststring + "=; expires=" + (new Date(0)).toUTCString();
    return cookieEnabled;
  }

  function encodeReturnUrl(str) {
    var x, i;
    var result = "";
    for (i=0; i<str.length; i++) {
      x = str.charCodeAt(i).toString(16);
      result += ("000"+x).slice(-4);
    }
    return result;
  }

  if (firstRun) {
    window.addEventListener("message", function(event) {

      if (!event.origin.match(/pol.is$/)) {
        return;
      }

      if (event.data === "cookieRedirect" && cookiesEnabledAtTopLevel()) {
        // temporarily redirect to polis, which will set a cookie and redirect back
        window.location = "https://embed.pol.is/api/v3/launchPrep?dest=" + encodeReturnUrl(window.location+"");
      }
    }, false);
  }

  // Add iframes to any polis divs that don't already have iframes.
  // (check needed since this script may be included multiple times)
  var polisDivs = document.getElementsByClassName("polis");
  for (var i = 0; i < polisDivs.length; i++) {
      var d = polisDivs[i];
      if (d.children && d.children.length) {
          // already populated
      } else {
         var config = getConfig(d);
         createPolisIframe(d, config);
      }
  }
}());

<commit_msg>TEMP point embedded to preprod<commit_after>(function() {
  var firstRun = !window.polis;
  window.polis = window.polis || {};

  function getConfig(d) {
     return {
         conversation_id: d.getAttribute("data-conversation_id"),
         height: d.getAttribute("data-height"),
         demo: d.getAttribute("data-demo")
     };
  }

  function createPolisIframe(parent, o) {
    var iframe = document.createElement("iframe");
    var path = [];
    if (o.demo) {
      path.push("demo");
    }
    path.push(o.conversation_id);
    iframe.src = "https://preprod.pol.is/"+ path.join("/");
    iframe.width = "100%"; // may be constrained by parent div
    iframe.height = o.height || 930;
    iframe.style.border = "1px solid #ccc";
    iframe.style.borderRadius = "4px";
    parent.appendChild(iframe);
  }

  function cookiesEnabledAtTopLevel() {
    // create a temporary cookie
    var soon = new Date(Date.now() + 1000).toUTCString();
    var teststring = "_polistest_cookiesenabled";
    document.cookie = teststring + "=1; expires=" + soon;
    // see if it worked
    var cookieEnabled = document.cookie.indexOf(teststring) != -1;
    // clear the cookie
    document.cookie = teststring + "=; expires=" + (new Date(0)).toUTCString();
    return cookieEnabled;
  }

  function encodeReturnUrl(str) {
    var x, i;
    var result = "";
    for (i=0; i<str.length; i++) {
      x = str.charCodeAt(i).toString(16);
      result += ("000"+x).slice(-4);
    }
    return result;
  }

  if (firstRun) {
    window.addEventListener("message", function(event) {

      if (!event.origin.match(/pol.is$/)) {
        return;
      }

      if (event.data === "cookieRedirect" && cookiesEnabledAtTopLevel()) {
        // temporarily redirect to polis, which will set a cookie and redirect back
        window.location = "https://embed.pol.is/api/v3/launchPrep?dest=" + encodeReturnUrl(window.location+"");
      }
    }, false);
  }

  // Add iframes to any polis divs that don't already have iframes.
  // (check needed since this script may be included multiple times)
  var polisDivs = document.getElementsByClassName("polis");
  for (var i = 0; i < polisDivs.length; i++) {
      var d = polisDivs[i];
      if (d.children && d.children.length) {
          // already populated
      } else {
         var config = getConfig(d);
         createPolisIframe(d, config);
      }
  }
}());

<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

var Song = new Schema({
  name: String
});
var Songs = mongoose.model('Song', Song);

// List
router.get('/', function(req, res) {
  Songs.find({}, function(err, song) {
    if(err) {
      console.log('Error');
    }
    res.send('LIST');
  });
});

// Create
router.post('/', function(req, res) {
  req.db.collection(tableName).find().toArray(function(err, results) {
    if(err) {
      console.log('Error');
    }
    res.send('CREATE');
  });
});

// Read
router.get('/:id', function(req, res) {
  req.db.collection(tableName).find().toArray(function(err, results) {
    if(err) {
      console.log('Error');
    }
    res.send('READ');
  });
});

// Update
router.put('/:id', function(req, res) {
  req.db.collection(tableName).save(req.body, function(req, res) {
    if(err) {
      console.log('Error');
    }
    res.send('UPDATE');
  });
});

// DELETE
router.delete('/:id', function(req, res) {
  req.db.collection(tableName).delete(req.body, function(req, res) {
    if(err) {
      console.log('Error');
    }
    res.send('DELETE');
  });
});

module.exports = router;
<commit_msg>Update songs.js<commit_after>var express = require('express');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var router = express.Router();

var Song = new Schema({
  name: String
});
var Songs = mongoose.model('Song', Song);

// List
router.get('/', function(req, res) {
  Songs.find({}, function(err, song) {
    if(err) {
      console.log('Error');
    }
    res.send('LIST');
  });
});

// Create
router.post('/', function(req, res) {
  req.db.collection(tableName).find().toArray(function(err, results) {
    if(err) {
      console.log('Error');
    }
    res.send('CREATE');
  });
});

// Read
router.get('/:id', function(req, res) {
  req.db.collection(tableName).find().toArray(function(err, results) {
    if(err) {
      console.log('Error');
    }
    res.send('READ');
  });
});

// Update
router.put('/:id', function(req, res) {
  req.db.collection(tableName).save(req.body, function(req, res) {
    if(err) {
      console.log('Error');
    }
    res.send('UPDATE');
  });
});

// DELETE
router.delete('/:id', function(req, res) {
  req.db.collection(tableName).delete(req.body, function(req, res) {
    if(err) {
      console.log('Error');
    }
    res.send('DELETE');
  });
});

module.exports = router;
<|endoftext|>


<commit_before>import Scroll from '../mixin/scroll';
import {
    $$,
    css,
    filter,
    data as getData,
    observeIntersection,
    once,
    removeClass,
    removeClasses,
    toggleClass,
    toPx,
    trigger,
} from 'uikit-util';

const stateKey = '_ukScrollspy';
export default {
    mixins: [Scroll],

    args: 'cls',

    props: {
        cls: String,
        target: String,
        hidden: Boolean,
        offsetTop: Number,
        offsetLeft: Number,
        repeat: Boolean,
        delay: Number,
    },

    data: () => ({
        cls: '',
        target: false,
        hidden: true,
        offsetTop: 0,
        offsetLeft: 0,
        repeat: false,
        delay: 0,
        inViewClass: 'uk-scrollspy-inview',
    }),

    computed: {
        elements: {
            get({ target }, $el) {
                return target ? $$(target, $el) : [$el];
            },

            watch(elements, prev) {
                if (this.hidden) {
                    css(filter(elements, `:not(.${this.inViewClass})`), 'visibility', 'hidden');
                }

                if (prev) {
                    this.$reset();
                }
            },

            immediate: true,
        },
    },

    connected() {
        this.registerObserver(
            observeIntersection(
                this.elements,
                (records) => {
                    for (const { target: el, isIntersecting } of records) {
                        if (!el[stateKey]) {
                            el[stateKey] = { cls: getData(el, 'uk-scrollspy-class') || this.cls };
                        }

                        if (!this.repeat && el[stateKey].show) {
                            continue;
                        }

                        el[stateKey].show = isIntersecting;
                    }

                    this.$emit();
                },
                {
                    rootMargin: `${toPx(this.offsetTop, 'height') - 1}px ${
                        toPx(this.offsetLeft, 'width') - 1
                    }px`,
                },
                false
            )
        );
    },

    disconnected() {
        for (const el of this.elements) {
            removeClass(el, this.inViewClass, el[stateKey]?.cls || '');
            delete el[stateKey];
        }
    },

    update: [
        {
            write(data) {
                for (const el of this.elements) {
                    const state = el[stateKey];

                    if (!state) {
                        continue;
                    }

                    if (state.show && !state.inview && !state.queued) {
                        state.queued = true;

                        data.promise = (data.promise || Promise.resolve())
                            .then(() => new Promise((resolve) => setTimeout(resolve, this.delay)))
                            .then(() => {
                                this.toggle(el, true);
                                setTimeout(() => {
                                    state.queued = false;
                                    this.$emit();
                                }, 300);
                            });
                    } else if (!state.show && state.inview && !state.queued && this.repeat) {
                        this.toggle(el, false);
                    }
                }
            },
        },
    ],

    methods: {
        toggle(el, inview) {
            const state = el[stateKey];

            state.off?.();

            css(el, 'visibility', !inview && this.hidden ? 'hidden' : '');

            toggleClass(el, this.inViewClass, inview);
            toggleClass(el, state.cls);

            if (/\buk-animation-/.test(state.cls)) {
                const removeAnimationClasses = () => removeClasses(el, 'uk-animation-[\\w-]+');
                if (inview) {
                    state.off = once(el, 'animationcancel animationend', removeAnimationClasses);
                } else {
                    removeAnimationClasses();
                }
            }

            trigger(el, inview ? 'inview' : 'outview');

            state.inview = inview;

            this.$update(el);
        },
    },
};
<commit_msg>refactor: scrollspy no longer stores its state on the element itself<commit_after>import Scroll from '../mixin/scroll';
import {
    $$,
    css,
    filter,
    data as getData,
    observeIntersection,
    once,
    removeClass,
    removeClasses,
    toggleClass,
    toPx,
    trigger,
} from 'uikit-util';

export default {
    mixins: [Scroll],

    args: 'cls',

    props: {
        cls: String,
        target: String,
        hidden: Boolean,
        offsetTop: Number,
        offsetLeft: Number,
        repeat: Boolean,
        delay: Number,
    },

    data: () => ({
        cls: '',
        target: false,
        hidden: true,
        offsetTop: 0,
        offsetLeft: 0,
        repeat: false,
        delay: 0,
        inViewClass: 'uk-scrollspy-inview',
    }),

    computed: {
        elements: {
            get({ target }, $el) {
                return target ? $$(target, $el) : [$el];
            },

            watch(elements, prev) {
                if (this.hidden) {
                    css(filter(elements, `:not(.${this.inViewClass})`), 'visibility', 'hidden');
                }

                if (prev) {
                    this.$reset();
                }
            },

            immediate: true,
        },
    },

    connected() {
        this._data.elements = new Map();
        this.registerObserver(
            observeIntersection(
                this.elements,
                (records) => {
                    const elements = this._data.elements;
                    for (const { target: el, isIntersecting } of records) {
                        if (!elements.has(el)) {
                            elements.set(el, {
                                cls: getData(el, 'uk-scrollspy-class') || this.cls,
                            });
                        }

                        const state = elements.get(el);
                        if (!this.repeat && state.show) {
                            continue;
                        }

                        state.show = isIntersecting;
                    }

                    this.$emit();
                },
                {
                    rootMargin: `${toPx(this.offsetTop, 'height') - 1}px ${
                        toPx(this.offsetLeft, 'width') - 1
                    }px`,
                },
                false
            )
        );
    },

    disconnected() {
        for (const [el, state] of this._data.elements.entries()) {
            removeClass(el, this.inViewClass, state?.cls || '');
        }
    },

    update: [
        {
            write(data) {
                for (const [el, state] of data.elements.entries()) {
                    if (state.show && !state.inview && !state.queued) {
                        state.queued = true;

                        data.promise = (data.promise || Promise.resolve())
                            .then(() => new Promise((resolve) => setTimeout(resolve, this.delay)))
                            .then(() => {
                                this.toggle(el, true);
                                setTimeout(() => {
                                    state.queued = false;
                                    this.$emit();
                                }, 300);
                            });
                    } else if (!state.show && state.inview && !state.queued && this.repeat) {
                        this.toggle(el, false);
                    }
                }
            },
        },
    ],

    methods: {
        toggle(el, inview) {
            const state = this._data.elements.get(el);

            state.off?.();

            css(el, 'visibility', !inview && this.hidden ? 'hidden' : '');

            toggleClass(el, this.inViewClass, inview);
            toggleClass(el, state.cls);

            if (/\buk-animation-/.test(state.cls)) {
                const removeAnimationClasses = () => removeClasses(el, 'uk-animation-[\\w-]+');
                if (inview) {
                    state.off = once(el, 'animationcancel animationend', removeAnimationClasses);
                } else {
                    removeAnimationClasses();
                }
            }

            trigger(el, inview ? 'inview' : 'outview');

            state.inview = inview;

            this.$update(el);
        },
    },
};
<|endoftext|>


<commit_before>'use strict';

var $ = require('jquery');
var klass = require('klass');

module.exports = klass({
    initialize: function ()
    {
        this.comicPagesUrl = '/comic/list';
        this.fileListUrl   = '/file-list';
    },
    setApiRoot: function (apiRoot) {
        this.apiRoot = apiRoot;
    },
    setComicPagesUrl: function (url)
    {
        this.comicPagesUrl = url
    },
    setFileListUrl: function (url)
    {
        this.fileListUrl = url
    },
    getFileListUrl: function () {
        return this.apiRoot + this.fileListUrl;
    },
    getComicPagesUrl: function () {
        return this.apiRoot + this.comicPagesUrl;
    },
    getFileList: function (path, cb)
    {
        $.ajax({
            url: this.getFileListUrl(),
            data: {directory: path},
            success: function (returnValue) {
                if (returnValue.status === 0) {
                    cb(null, path, returnValue.data);
                } else {
                    cb(new Error('Bad Status'));
                }
            },
            error: function (err) {
                cb(err);
            }
        });
    },
    getComicPages: function (path, cb)
    {
        $.ajax({
            url: this.getComicPagesUrl(),
            data: {file: path},
            success: function (returnValue) {
                if (returnValue.status === 0) {
                    cb(null, path, returnValue.data);
                } else {
                    cb(new Error('Bad Status'));
                }
            },
            error: function (err) {
                cb(err);
            }
        });
    }
});
<commit_msg>Fix CORS support<commit_after>'use strict';

var $ = require('jquery');
var klass = require('klass');

$.support.cors = true;

module.exports = klass({
    initialize: function ()
    {
        this.comicPagesUrl = '/comic/list';
        this.fileListUrl   = '/file-list';
    },
    setApiRoot: function (apiRoot) {
        this.apiRoot = apiRoot;
    },
    setComicPagesUrl: function (url)
    {
        this.comicPagesUrl = url
    },
    setFileListUrl: function (url)
    {
        this.fileListUrl = url
    },
    getFileListUrl: function () {
        return this.apiRoot + this.fileListUrl;
    },
    getComicPagesUrl: function () {
        return this.apiRoot + this.comicPagesUrl;
    },
    getFileList: function (path, cb)
    {
        $.ajax({
            url: this.getFileListUrl(),
            data: {directory: path},
            success: function (returnValue) {
                if (returnValue.status === 0) {
                    cb(null, path, returnValue.data);
                } else {
                    cb(new Error('Bad Status'));
                }
            },
            error: function (err) {
                cb(err);
            }
        });
    },
    getComicPages: function (path, cb)
    {
        $.ajax({
            url: this.getComicPagesUrl(),
            data: {file: path},
            success: function (returnValue) {
                if (returnValue.status === 0) {
                    cb(null, path, returnValue.data);
                } else {
                    cb(new Error('Bad Status'));
                }
            },
            error: function (err) {
                cb(err);
            }
        });
    }
});
<|endoftext|>


<commit_before>export function getSupporterCategories () {
  return [{
    'name': 'I have experienced homelessness',
    'listOrder': 1,
    'examplePledges': [
      'Joining an action group to design homelessness services.',
      'Volunteering to help a local organisation.',
      'Being an ambassador for the Charter.',
      'Sharing my experiences of using services.',
      'Sharing my story to inform others about homelessness.',
      'Including my name on the charter supporters database.'
    ]
  },{
    'name': 'I am an individual that wants to help',
    'listOrder': 1,
    'examplePledges': [
      'Making a donation to Big Change Manchester.',
      'Registering as a volunteer on Street Support',
      'Asking my employer to pledge to support the Charter',
      'Including my name on the charter supporters database.'
    ]
  },{
    'name': 'I represent a business',
    'listOrder': 1,
    'examplePledges': [
      'Offering employment opportunities through entry level jobs.',
      'Offering skills and resources to local organisations.',
      'Informing our staff about homelessness and alternative giving.',
      'Raising money for local organisations.',
      'Donating to the Big Change campaign.',
      'Helping with marketing to raise awareness of the charter.',
      'Helping to make it easier to open a bank account.',
      'Ensuring our facilities are accessible to homeless people.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I represent a service provider',
    'listOrder': 1,
    'examplePledges': [
      'Reviewing our services in line with the charter vision and values.',
      'Bringing representatives to relevant action groups.',
      'Ensuring our staff sign up to the values of the charter,',
      'Including service users as we design our services.',
      'Regularly invite feedback and listen to your service users.',
      'Providing evening services.',
      'Increasing turn around time in hostels so more rooms are available.',
      'Committing to a \'no wrong door\' policy.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I am a public sector decision maker',
    'listOrder': 1,
    'examplePledges': [
      'Ensuring our facilities are accessible to people who are homeless.',
      'Treating people in line with the values in the charter.',
      'Training our staff on the charter and the rights of homeless people.',
      'Providing training opportunities.',
      'Ensuring key decision makers visit services.',
      'Including homeless people in meetings.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I am part of a community or faith-based group',
    'listOrder': 1,
    'examplePledges': [
      'Providing emergency accommodation for rough sleepers all year round.',
      'Informing my community about homelessness and alternative giving.',
      'Donating or fundraising for local organisations.',
      'Providing housing - supported housing or host families.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I represent an educational organisation',
    'listOrder': 1,
    'examplePledges': [
      'Making a donation to Big Change Manchester.',
      'Developing staff and students homelessness ambassadors.',
      'Providing student placements to increase capacity and innovation.',
      'Developing work placements and employment opportunities.',
      'Providing research capacity to evaluate the impact of the charter.',
      'Including my name on the charter supporters database.'
    ]
  }]
}<commit_msg>Add 'get informed' option.<commit_after>export function getSupporterCategories () {
  return [{
    'name': 'I have experienced homelessness',
    'listOrder': 1,
    'examplePledges': [
      'Joining an action group to design homelessness services.',
      'Volunteering to help a local organisation.',
      'Being an ambassador for the Charter.',
      'Sharing my experiences of using services.',
      'Sharing my story to inform others about homelessness.',
      'Including my name on the charter supporters database.'
    ]
  },{
    'name': 'I am an individual that wants to help',
    'listOrder': 1,
    'examplePledges': [
      'Making a donation to Big Change Manchester.',
      'Registering as a volunteer on Street Support',
      'Asking my employer to pledge to support the Charter',
      'Becoming more informed about homelessness, and what services are available',
      'Including my name on the charter supporters database.'
    ]
  },{
    'name': 'I represent a business',
    'listOrder': 1,
    'examplePledges': [
      'Offering employment opportunities through entry level jobs.',
      'Offering skills and resources to local organisations.',
      'Informing our staff about homelessness and alternative giving.',
      'Raising money for local organisations.',
      'Donating to the Big Change campaign.',
      'Helping with marketing to raise awareness of the charter.',
      'Helping to make it easier to open a bank account.',
      'Ensuring our facilities are accessible to homeless people.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I represent a service provider',
    'listOrder': 1,
    'examplePledges': [
      'Reviewing our services in line with the charter vision and values.',
      'Bringing representatives to relevant action groups.',
      'Ensuring our staff sign up to the values of the charter,',
      'Including service users as we design our services.',
      'Regularly invite feedback and listen to your service users.',
      'Providing evening services.',
      'Increasing turn around time in hostels so more rooms are available.',
      'Committing to a \'no wrong door\' policy.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I am a public sector decision maker',
    'listOrder': 1,
    'examplePledges': [
      'Ensuring our facilities are accessible to people who are homeless.',
      'Treating people in line with the values in the charter.',
      'Training our staff on the charter and the rights of homeless people.',
      'Providing training opportunities.',
      'Ensuring key decision makers visit services.',
      'Including homeless people in meetings.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I am part of a community or faith-based group',
    'listOrder': 1,
    'examplePledges': [
      'Providing emergency accommodation for rough sleepers all year round.',
      'Informing my community about homelessness and alternative giving.',
      'Donating or fundraising for local organisations.',
      'Providing housing - supported housing or host families.',
      'Including my name on the charter supporters database.'
    ]
  }, {
    'name': 'I represent an educational organisation',
    'listOrder': 1,
    'examplePledges': [
      'Making a donation to Big Change Manchester.',
      'Developing staff and students homelessness ambassadors.',
      'Providing student placements to increase capacity and innovation.',
      'Developing work placements and employment opportunities.',
      'Providing research capacity to evaluate the impact of the charter.',
      'Including my name on the charter supporters database.'
    ]
  }]
}<|endoftext|>


<commit_before>/**
 * Created by Tim Osadchiy on 15/08/2016.
 */

// ToDo: Error in console if yaml has got a wrong structure
// ToDo: Warning in console if some of the corners were not found in yaml

'use strict';

var yaml = require('js-yaml'),
    baseAttr = process.env.dataAttributeBase;

module.exports = function (onSuccess, onFailure) {
    var yamlPath = this.getAttribute(baseAttr) || this.src.substr(0, this.src.lastIndexOf(".")) + ".yaml";
    loadYaml(yamlPath, onSuccess, onFailure);
};

function loadYaml(path, success, error) {
    error = error || function () {
        };
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                var data = yaml.safeLoad(xhr.responseText);
                if (!dataIsValid(path, data)) {
                    error();
                } else {
                    success(data);
                }
            } else {
                error(xhr);
            }
        }
    };
    xhr.open("GET", path, true);
    xhr.send();
}

function dataIsValid(path, data) {
    return contextIsValid(path, data) &&
        linksAreValid(path, data) &&
        backStoryIsValid(path, data) &&
        creativeCommonsAreValid(path, data);
}

function contextIsValid(path, data) {
    if (!data.context) {
        console.warn(path, ': \'context\' is not defined');
    }
    if (data.context && Object.prototype.toString.call(data.context) != '[object Array]') {
        throw path + ': \'context\' must be a list';
    } else if (data.context) {
        var toDelete = [];
        for (var i = 0, l = data.context.length; i < l; i++) {
            var el = data.context[i];
            if (!el.src && !el.youtube_id) {
                console.warn(path, ': \'context\', element number', String(i + 1),
                    ' - \'src\' and \'youtube_id\' are not defined');
                toDelete.push(el);
            }
        }
        for (var i = 0, l = toDelete.length; i < l; i++) {
            var j = data.context.indexOf(toDelete[i]);
            data.context.splice(j, 1);
        }
    }
    return true;
}

function linksAreValid(path, data) {
    if (!data.links) {
        console.warn(path, ': \'links\' are not defined');
    }
    if (data.links && Object.prototype.toString.call(data.links) != '[object Array]') {
        throw path + ': \'links\' must be a list';
    } else if (data.links) {
        var toDelete = [];
        for (var i = 0, l = data.links.length; i < l; i++) {
            var el = data.links[i];
            if (!el.url) {
                console.warn(path, ': \'links\', element number', String(i + 1),
                    ' - \'url\' is not defined');
                toDelete.push(el);
            }
        }
        for (var i = 0, l = toDelete.length; i < l; i++) {
            var j = data.links.indexOf(toDelete[i]);
            data.links.splice(j, 1);
        }
    }
    return true;
}

function backStoryIsValid(path, data) {
    if (!data.backStory) {
        console.warn(path, ': \'backStory\' is not defined');
    }
    if (data.backStory && !data.backStory.text) {
        console.warn(path, ': \'backStory\' has no text');
        return true;
    }
    return true;
}

function creativeCommonsAreValid(path, data) {
    if (!data.creativeCommons) {
        console.warn(path, ': \'creativeCommons\' are not defined');
    }
    if (data.creativeCommons && !data.creativeCommons.copyright) {
        console.warn(path, ': \'creativeCommons\' - author is not defined');
        return true;
    }
    return true;
}<commit_msg>errors and warnings feedback on yaml structure in console<commit_after>/**
 * Created by Tim Osadchiy on 15/08/2016.
 */

'use strict';

var yaml = require('js-yaml'),
    baseAttr = process.env.dataAttributeBase;

module.exports = function (onSuccess, onFailure) {
    var yamlPath = this.getAttribute(baseAttr) || this.src.substr(0, this.src.lastIndexOf(".")) + ".yaml";
    loadYaml(yamlPath, onSuccess, onFailure);
};

function loadYaml(path, success, error) {
    error = error || function () {
        };
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                var data = yaml.safeLoad(xhr.responseText);
                if (!dataIsValid(path, data)) {
                    error();
                } else {
                    success(data);
                }
            } else {
                error(xhr);
            }
        }
    };
    xhr.open("GET", path, true);
    xhr.send();
}

function dataIsValid(path, data) {
    return contextIsValid(path, data) &&
        linksAreValid(path, data) &&
        backStoryIsValid(path, data) &&
        creativeCommonsAreValid(path, data);
}

function contextIsValid(path, data) {
    if (!data.context) {
        console.warn(path, ': \'context\' is not defined');
    }
    if (data.context && Object.prototype.toString.call(data.context) != '[object Array]') {
        throw path + ': \'context\' must be a list';
    } else if (data.context) {
        var toDelete = [];
        for (var i = 0, l = data.context.length; i < l; i++) {
            var el = data.context[i];
            if (!el.src && !el.youtube_id) {
                console.warn(path, ': \'context\', element number', String(i + 1),
                    ' - \'src\' and \'youtube_id\' are not defined');
                toDelete.push(el);
            }
        }
        for (var i = 0, l = toDelete.length; i < l; i++) {
            var j = data.context.indexOf(toDelete[i]);
            data.context.splice(j, 1);
        }
    }
    return true;
}

function linksAreValid(path, data) {
    if (!data.links) {
        console.warn(path, ': \'links\' are not defined');
    }
    if (data.links && Object.prototype.toString.call(data.links) != '[object Array]') {
        throw path + ': \'links\' must be a list';
    } else if (data.links) {
        var toDelete = [];
        for (var i = 0, l = data.links.length; i < l; i++) {
            var el = data.links[i];
            if (!el.url) {
                console.warn(path, ': \'links\', element number', String(i + 1),
                    ' - \'url\' is not defined');
                toDelete.push(el);
            }
        }
        for (var i = 0, l = toDelete.length; i < l; i++) {
            var j = data.links.indexOf(toDelete[i]);
            data.links.splice(j, 1);
        }
    }
    return true;
}

function backStoryIsValid(path, data) {
    if (!data.backStory) {
        console.warn(path, ': \'backStory\' is not defined');
    }
    if (data.backStory && !data.backStory.text) {
        console.warn(path, ': \'backStory\' has no text');
        return true;
    }
    return true;
}

function creativeCommonsAreValid(path, data) {
    if (!data.creativeCommons) {
        console.warn(path, ': \'creativeCommons\' are not defined');
    }
    if (data.creativeCommons && !data.creativeCommons.copyright) {
        console.warn(path, ': \'creativeCommons\' - author is not defined');
        return true;
    }
    return true;
}<|endoftext|>


<commit_before>import SearchBtn from '../extended/SearchBtn';
import TrashBtn from '../extended/TrashBtn';
import LoadBtn from '../extended/LoadBtn';
import BtnControl from '../extended/BtnControl';
import MsgHelper from '../extended/MsgHelper';
import m from '../utils/mobile';

export default function () {

  var ggl = new L.Google();

  this.addLayer(ggl);

  this._controlLayers = new L.Control.Layers({
    'Google': ggl
  });

  this.addControl(this._controlLayers);

  this.touchZoom.disable();
  this.doubleClickZoom.disable();
  //this.scrollWheelZoom.disable();
  this.boxZoom.disable();
  this.keyboard.disable();

  this.addControl(new SearchBtn());

  this._BtnControl = BtnControl;

  var trashBtn = new TrashBtn({
    btns: [
      {'className': 'fa fa-trash'}
    ]
  });

  var loadBtn = new LoadBtn({
    btns: [
      {'className': 'fa fa-arrow-circle-o-down load'}
    ]
  });

  var msgHelper = new MsgHelper({
    defaultMsg: this.options.text.clickToStartDrawPolygonOnMap
  });

  this.on('msgHelperAdded', (data) => {
    var msgContainer = data.control._titleContainer;

    this._msgContainer = msgContainer;
    var text = this.options.text;

    this.on('editor:marker_group_select', () => {

      this.off('editor:not_selected_marker_mouseover');
      this.on('editor:not_selected_marker_mouseover', () => {
        msgContainer.msg(text.clickToSelectEdges);
      });

      this.off('editor:selected_marker_mouseover');
      this.on('editor:selected_marker_mouseover', () => {
        msgContainer.msg(text.clickToRemoveAllSelectedEdges);
      });

      this.off('editor:selected_middle_marker_mouseover');
      this.on('editor:selected_middle_marker_mouseover', () => {
        msgContainer.msg(text.clickToAddNewEdges);
      });

      // on edit polygon
      this.off('editor:edit_polygon_mouseover');
      this.on('editor:edit_polygon_mouseover', () => {
        msgContainer.msg(text.clickToDrawInnerEdges);
      });

      this.off('editor:edit_polygon_mouseout');
      this.on('editor:edit_polygon_mouseout', () => {
        msgContainer.hide();
      });
      // on view polygon
      this.off('editor:view_polygon_mouseover');
      this.on('editor:view_polygon_mouseover', () => {
        msgContainer.msg(text.clickToEdit);
      });

      this.off('editor:view_polygon_mouseout');
      this.on('editor:view_polygon_mouseout', () => {
        msgContainer.hide();
      });
    });

    // hide msg
    this.off('editor:marker_mouseout');
    this.on('editor:marker_mouseout', () => {
      msgContainer.hide();
    });
    // on start draw polygon
    this.off('editor:first_marker_mouseover');
    this.on('editor:first_marker_mouseover', () => {
      msgContainer.msg(text.clickToJoinEdges);
    });
    // dblclick to join
    this.off('editor:last_marker_dblclick_mouseover');
    this.on('editor:last_marker_dblclick_mouseover', () => {
      msgContainer.msg(text.dblclickToJoinEdges);
    });

    this.on('editor:join_path', () => {
      msgContainer.msg(this.options.text.clickToRemoveAllSelectedEdges);
    });

    //todo: continue with option 'allowCorrectIntersection'
    this.on('editor:intersection_detected', (data) => {
      // msg
      if (data.intersection) {
        msgContainer.msg(this.options.text.intersection);
      } else {
        msgContainer.hide();
      }

      var selectedMarker = this.getSelectedMarker();

      if(!this.hasLayer(selectedMarker)) {
        return;
      }

      if(selectedMarker && !selectedMarker._mGroup.hasFirstMarker()) {
        //set marker style
        if (data.intersection) {
          //set 'error' style
          selectedMarker.setIntersectedStyle();
        } else {
          //restore style
          selectedMarker.resetStyle();
          //restore other markers which are also need to reset style
          var markersToReset = selectedMarker._mGroup.getLayers().filter((layer) => {
            return L.DomUtil.hasClass(layer._icon, 'm-editor-intersection-div-icon');
          });

          markersToReset.map((marker) => marker.resetStyle());
        }
      }
    });
  });

  if (!m.isMobileBrowser()) {
    this.addControl(msgHelper);
    this.addControl(loadBtn);
  }

  this.addControl(trashBtn);
}<commit_msg>check tags<commit_after>import SearchBtn from '../extended/SearchBtn';
import TrashBtn from '../extended/TrashBtn';
import LoadBtn from '../extended/LoadBtn';
import BtnControl from '../extended/BtnControl';
import MsgHelper from '../extended/MsgHelper';
import m from '../utils/mobile';

export default function () {

  /* check last update */

  var ggl = new L.Google();

  this.addLayer(ggl);

  this._controlLayers = new L.Control.Layers({
    'Google': ggl
  });

  this.addControl(this._controlLayers);

  this.touchZoom.disable();
  this.doubleClickZoom.disable();
  //this.scrollWheelZoom.disable();
  this.boxZoom.disable();
  this.keyboard.disable();

  this.addControl(new SearchBtn());

  this._BtnControl = BtnControl;

  var trashBtn = new TrashBtn({
    btns: [
      {'className': 'fa fa-trash'}
    ]
  });

  var loadBtn = new LoadBtn({
    btns: [
      {'className': 'fa fa-arrow-circle-o-down load'}
    ]
  });

  var msgHelper = new MsgHelper({
    defaultMsg: this.options.text.clickToStartDrawPolygonOnMap
  });

  this.on('msgHelperAdded', (data) => {
    var msgContainer = data.control._titleContainer;

    this._msgContainer = msgContainer;
    var text = this.options.text;

    this.on('editor:marker_group_select', () => {

      this.off('editor:not_selected_marker_mouseover');
      this.on('editor:not_selected_marker_mouseover', () => {
        msgContainer.msg(text.clickToSelectEdges);
      });

      this.off('editor:selected_marker_mouseover');
      this.on('editor:selected_marker_mouseover', () => {
        msgContainer.msg(text.clickToRemoveAllSelectedEdges);
      });

      this.off('editor:selected_middle_marker_mouseover');
      this.on('editor:selected_middle_marker_mouseover', () => {
        msgContainer.msg(text.clickToAddNewEdges);
      });

      // on edit polygon
      this.off('editor:edit_polygon_mouseover');
      this.on('editor:edit_polygon_mouseover', () => {
        msgContainer.msg(text.clickToDrawInnerEdges);
      });

      this.off('editor:edit_polygon_mouseout');
      this.on('editor:edit_polygon_mouseout', () => {
        msgContainer.hide();
      });
      // on view polygon
      this.off('editor:view_polygon_mouseover');
      this.on('editor:view_polygon_mouseover', () => {
        msgContainer.msg(text.clickToEdit);
      });

      this.off('editor:view_polygon_mouseout');
      this.on('editor:view_polygon_mouseout', () => {
        msgContainer.hide();
      });
    });

    // hide msg
    this.off('editor:marker_mouseout');
    this.on('editor:marker_mouseout', () => {
      msgContainer.hide();
    });
    // on start draw polygon
    this.off('editor:first_marker_mouseover');
    this.on('editor:first_marker_mouseover', () => {
      msgContainer.msg(text.clickToJoinEdges);
    });
    // dblclick to join
    this.off('editor:last_marker_dblclick_mouseover');
    this.on('editor:last_marker_dblclick_mouseover', () => {
      msgContainer.msg(text.dblclickToJoinEdges);
    });

    this.on('editor:join_path', () => {
      msgContainer.msg(this.options.text.clickToRemoveAllSelectedEdges);
    });

    //todo: continue with option 'allowCorrectIntersection'
    this.on('editor:intersection_detected', (data) => {
      // msg
      if (data.intersection) {
        msgContainer.msg(this.options.text.intersection);
      } else {
        msgContainer.hide();
      }

      var selectedMarker = this.getSelectedMarker();

      if(!this.hasLayer(selectedMarker)) {
        return;
      }

      if(selectedMarker && !selectedMarker._mGroup.hasFirstMarker()) {
        //set marker style
        if (data.intersection) {
          //set 'error' style
          selectedMarker.setIntersectedStyle();
        } else {
          //restore style
          selectedMarker.resetStyle();
          //restore other markers which are also need to reset style
          var markersToReset = selectedMarker._mGroup.getLayers().filter((layer) => {
            return L.DomUtil.hasClass(layer._icon, 'm-editor-intersection-div-icon');
          });

          markersToReset.map((marker) => marker.resetStyle());
        }
      }
    });
  });

  if (!m.isMobileBrowser()) {
    this.addControl(msgHelper);
    this.addControl(loadBtn);
  }

  this.addControl(trashBtn);
}<|endoftext|>