<commit_before>// # Ghost Head Helper
// Usage: `{{ghost_head}}`
//
// Outputs scripts and other assets at the top of a Ghost theme
const {metaData, escapeExpression, SafeString, logging, settingsCache, config, blogIcon, labs, urlUtils} = require('../services/proxy');
const _ = require('lodash');
const debug = require('ghost-ignition').debug('ghost_head');

const getMetaData = metaData.get;
const getAssetUrl = metaData.getAssetUrl;

function writeMetaTag(property, content, type) {
    type = type || property.substring(0, 7) === 'twitter' ? 'name' : 'property';
    return '<meta ' + type + '="' + property + '" content="' + content + '" />';
}

function finaliseStructuredData(metaData) {
    const head = [];

    _.each(metaData.structuredData, function (content, property) {
        if (property === 'article:tag') {
            _.each(metaData.keywords, function (keyword) {
                if (keyword !== '') {
                    keyword = escapeExpression(keyword);
                    head.push(writeMetaTag(property,
                        escapeExpression(keyword)));
                }
            });
            head.push('');
        } else if (content !== null && content !== undefined) {
            head.push(writeMetaTag(property,
                escapeExpression(content)));
        }
    });

    return head;
}

function getMembersHelper() {
    const stripePaymentProcessor = settingsCache.get('members_subscription_settings').paymentProcessors.find(
        paymentProcessor => paymentProcessor.adapter === 'stripe'
    );
    const stripeSecretToken = stripePaymentProcessor.config.secret_token;
    const stripePublicToken = stripePaymentProcessor.config.public_token;

    const stripeConnectIntegration = settingsCache.get('stripe_connect_integration');

    let membersHelper = `<script defer src="${getAssetUrl('public/members.js', true)}"></script>`;
    if (config.get('enableDeveloperExperiments')) {
        const siteUrl = urlUtils.getSiteUrl().replace(/\/$/, '');
        membersHelper = `<meta name="ghost:site" content='${siteUrl}' />`;
        membersHelper += `<script defer src="https://unpkg.com/@tryghost/members-js@latest/umd/members.min.js"></script>`;
    }
    if ((!!stripeSecretToken && !!stripePublicToken) || !!stripeConnectIntegration.account_id) {
        membersHelper += '<script src="https://js.stripe.com/v3/"></script>';
    }
    return membersHelper;
}

/**
 * **NOTE**
 * Express adds `_locals`, see https://github.com/expressjs/express/blob/4.15.4/lib/response.js#L962.
 * But `options.data.root.context` is available next to `root._locals.context`, because
 * Express creates a `renderOptions` object, see https://github.com/expressjs/express/blob/4.15.4/lib/application.js#L554
 * and merges all locals to the root of the object. Very confusing, because the data is available in different layers.
 *
 * Express forwards the data like this to the hbs engine:
 * {
 *   post: {},             - res.render('view', databaseResponse)
 *   context: ['post'],    - from res.locals
 *   safeVersion: '1.x',   - from res.locals
 *   _locals: {
 *     context: ['post'],
 *     safeVersion: '1.x'
 *   }
 * }
 *
 * hbs forwards the data to any hbs helper like this
 * {
 *   data: {
 *     site: {},
 *     labs: {},
 *     config: {},
 *     root: {
 *       post: {},
 *       context: ['post'],
 *       locals: {...}
 *     }
 *  }
 *
 * `site`, `labs` and `config` are the templateOptions, search for `hbs.updateTemplateOptions` in the code base.
 *  Also see how the root object gets created, https://github.com/wycats/handlebars.js/blob/v4.0.6/lib/handlebars/runtime.js#L259
 */
// We use the name ghost_head to match the helper for consistency:
module.exports = function ghost_head(options) { // eslint-disable-line camelcase
    debug('begin');

    // if server error page do nothing
    if (options.data.root.statusCode >= 500) {
        return;
    }

    const head = [];
    const dataRoot = options.data.root;
    const context = dataRoot._locals.context ? dataRoot._locals.context : null;
    const safeVersion = dataRoot._locals.safeVersion;
    const postCodeInjection = dataRoot && dataRoot.post ? dataRoot.post.codeinjection_head : null;
    const globalCodeinjection = settingsCache.get('ghost_head');
    const useStructuredData = !config.isPrivacyDisabled('useStructuredData');
    const referrerPolicy = config.get('referrerPolicy') ? config.get('referrerPolicy') : 'no-referrer-when-downgrade';
    const favicon = blogIcon.getIconUrl();
    const iconType = blogIcon.getIconType(favicon);

    debug('preparation complete, begin fetch');

    /**
     * @TODO:
     *   - getMetaData(dataRoot, dataRoot) -> yes that looks confusing!
     *   - there is a very mixed usage of `data.context` vs. `root.context` vs `root._locals.context` vs. `this.context`
     *   - NOTE: getMetaData won't live here anymore soon, see https://github.com/TryGhost/Ghost/issues/8995
     *   - therefor we get rid of using `getMetaData(this, dataRoot)`
     *   - dataRoot has access to *ALL* locals, see function description
     *   - it should not break anything
     */
    return getMetaData(dataRoot, dataRoot)
        .then(function handleMetaData(metaData) {
            debug('end fetch');

            if (context) {
                // head is our main array that holds our meta data
                if (metaData.metaDescription && metaData.metaDescription.length > 0) {
                    head.push('<meta name="description" content="' + escapeExpression(metaData.metaDescription) + '" />');
                }

                head.push('<link rel="shortcut icon" href="' + favicon + '" type="image/' + iconType + '" />');
                head.push('<link rel="canonical" href="' +
                    escapeExpression(metaData.canonicalUrl) + '" />');
                head.push('<meta name="referrer" content="' + referrerPolicy + '" />');

                // don't allow indexing of preview URLs!
                if (_.includes(context, 'preview')) {
                    head.push(writeMetaTag('robots', 'noindex,nofollow', 'name'));
                }

                // show amp link in post when 1. we are not on the amp page and 2. amp is enabled
                if (_.includes(context, 'post') && !_.includes(context, 'amp') && settingsCache.get('amp')) {
                    head.push('<link rel="amphtml" href="' +
                        escapeExpression(metaData.ampUrl) + '" />');
                }

                if (metaData.previousUrl) {
                    head.push('<link rel="prev" href="' +
                        escapeExpression(metaData.previousUrl) + '" />');
                }

                if (metaData.nextUrl) {
                    head.push('<link rel="next" href="' +
                        escapeExpression(metaData.nextUrl) + '" />');
                }

                if (!_.includes(context, 'paged') && useStructuredData) {
                    head.push('');
                    head.push.apply(head, finaliseStructuredData(metaData));
                    head.push('');

                    if (metaData.schema) {
                        head.push('<script type="application/ld+json">\n' +
                            JSON.stringify(metaData.schema, null, '    ') +
                            '\n    </script>\n');
                    }
                }

                if (!_.includes(context, 'amp') && labs.isSet('members')) {
                    head.push(getMembersHelper());
                }
            }

            head.push('<meta name="generator" content="Ghost ' +
                escapeExpression(safeVersion) + '" />');

            head.push('<link rel="alternate" type="application/rss+xml" title="' +
                escapeExpression(metaData.site.title) + '" href="' +
                escapeExpression(metaData.rssUrl) + '" />');

            // no code injection for amp context!!!
            if (!_.includes(context, 'amp')) {
                if (!_.isEmpty(globalCodeinjection)) {
                    head.push(globalCodeinjection);
                }

                if (!_.isEmpty(postCodeInjection)) {
                    head.push(postCodeInjection);
                }
            }
            debug('end');
            return new SafeString(head.join('\n    ').trim());
        })
        .catch(function handleError(err) {
            logging.error(err);

            // Return what we have so far (currently nothing)
            return new SafeString(head.join('\n    ').trim());
        });
};
<commit_msg>Refactored site url data attribute for new members script<commit_after>// # Ghost Head Helper
// Usage: `{{ghost_head}}`
//
// Outputs scripts and other assets at the top of a Ghost theme
const {metaData, escapeExpression, SafeString, logging, settingsCache, config, blogIcon, labs, urlUtils} = require('../services/proxy');
const _ = require('lodash');
const debug = require('ghost-ignition').debug('ghost_head');

const getMetaData = metaData.get;
const getAssetUrl = metaData.getAssetUrl;

function writeMetaTag(property, content, type) {
    type = type || property.substring(0, 7) === 'twitter' ? 'name' : 'property';
    return '<meta ' + type + '="' + property + '" content="' + content + '" />';
}

function finaliseStructuredData(metaData) {
    const head = [];

    _.each(metaData.structuredData, function (content, property) {
        if (property === 'article:tag') {
            _.each(metaData.keywords, function (keyword) {
                if (keyword !== '') {
                    keyword = escapeExpression(keyword);
                    head.push(writeMetaTag(property,
                        escapeExpression(keyword)));
                }
            });
            head.push('');
        } else if (content !== null && content !== undefined) {
            head.push(writeMetaTag(property,
                escapeExpression(content)));
        }
    });

    return head;
}

function getMembersHelper() {
    const stripePaymentProcessor = settingsCache.get('members_subscription_settings').paymentProcessors.find(
        paymentProcessor => paymentProcessor.adapter === 'stripe'
    );
    const stripeSecretToken = stripePaymentProcessor.config.secret_token;
    const stripePublicToken = stripePaymentProcessor.config.public_token;

    const stripeConnectIntegration = settingsCache.get('stripe_connect_integration');

    let membersHelper = `<script defer src="${getAssetUrl('public/members.js', true)}"></script>`;
    if (config.get('enableDeveloperExperiments')) {
        membersHelper += `<script defer src="https://unpkg.com/@tryghost/members-js@latest/umd/members.min.js" data-ghost="${urlUtils.getSiteUrl()}"></script>`;
    }
    if ((!!stripeSecretToken && !!stripePublicToken) || !!stripeConnectIntegration.account_id) {
        membersHelper += '<script src="https://js.stripe.com/v3/"></script>';
    }
    return membersHelper;
}

/**
 * **NOTE**
 * Express adds `_locals`, see https://github.com/expressjs/express/blob/4.15.4/lib/response.js#L962.
 * But `options.data.root.context` is available next to `root._locals.context`, because
 * Express creates a `renderOptions` object, see https://github.com/expressjs/express/blob/4.15.4/lib/application.js#L554
 * and merges all locals to the root of the object. Very confusing, because the data is available in different layers.
 *
 * Express forwards the data like this to the hbs engine:
 * {
 *   post: {},             - res.render('view', databaseResponse)
 *   context: ['post'],    - from res.locals
 *   safeVersion: '1.x',   - from res.locals
 *   _locals: {
 *     context: ['post'],
 *     safeVersion: '1.x'
 *   }
 * }
 *
 * hbs forwards the data to any hbs helper like this
 * {
 *   data: {
 *     site: {},
 *     labs: {},
 *     config: {},
 *     root: {
 *       post: {},
 *       context: ['post'],
 *       locals: {...}
 *     }
 *  }
 *
 * `site`, `labs` and `config` are the templateOptions, search for `hbs.updateTemplateOptions` in the code base.
 *  Also see how the root object gets created, https://github.com/wycats/handlebars.js/blob/v4.0.6/lib/handlebars/runtime.js#L259
 */
// We use the name ghost_head to match the helper for consistency:
module.exports = function ghost_head(options) { // eslint-disable-line camelcase
    debug('begin');

    // if server error page do nothing
    if (options.data.root.statusCode >= 500) {
        return;
    }

    const head = [];
    const dataRoot = options.data.root;
    const context = dataRoot._locals.context ? dataRoot._locals.context : null;
    const safeVersion = dataRoot._locals.safeVersion;
    const postCodeInjection = dataRoot && dataRoot.post ? dataRoot.post.codeinjection_head : null;
    const globalCodeinjection = settingsCache.get('ghost_head');
    const useStructuredData = !config.isPrivacyDisabled('useStructuredData');
    const referrerPolicy = config.get('referrerPolicy') ? config.get('referrerPolicy') : 'no-referrer-when-downgrade';
    const favicon = blogIcon.getIconUrl();
    const iconType = blogIcon.getIconType(favicon);

    debug('preparation complete, begin fetch');

    /**
     * @TODO:
     *   - getMetaData(dataRoot, dataRoot) -> yes that looks confusing!
     *   - there is a very mixed usage of `data.context` vs. `root.context` vs `root._locals.context` vs. `this.context`
     *   - NOTE: getMetaData won't live here anymore soon, see https://github.com/TryGhost/Ghost/issues/8995
     *   - therefor we get rid of using `getMetaData(this, dataRoot)`
     *   - dataRoot has access to *ALL* locals, see function description
     *   - it should not break anything
     */
    return getMetaData(dataRoot, dataRoot)
        .then(function handleMetaData(metaData) {
            debug('end fetch');

            if (context) {
                // head is our main array that holds our meta data
                if (metaData.metaDescription && metaData.metaDescription.length > 0) {
                    head.push('<meta name="description" content="' + escapeExpression(metaData.metaDescription) + '" />');
                }

                head.push('<link rel="shortcut icon" href="' + favicon + '" type="image/' + iconType + '" />');
                head.push('<link rel="canonical" href="' +
                    escapeExpression(metaData.canonicalUrl) + '" />');
                head.push('<meta name="referrer" content="' + referrerPolicy + '" />');

                // don't allow indexing of preview URLs!
                if (_.includes(context, 'preview')) {
                    head.push(writeMetaTag('robots', 'noindex,nofollow', 'name'));
                }

                // show amp link in post when 1. we are not on the amp page and 2. amp is enabled
                if (_.includes(context, 'post') && !_.includes(context, 'amp') && settingsCache.get('amp')) {
                    head.push('<link rel="amphtml" href="' +
                        escapeExpression(metaData.ampUrl) + '" />');
                }

                if (metaData.previousUrl) {
                    head.push('<link rel="prev" href="' +
                        escapeExpression(metaData.previousUrl) + '" />');
                }

                if (metaData.nextUrl) {
                    head.push('<link rel="next" href="' +
                        escapeExpression(metaData.nextUrl) + '" />');
                }

                if (!_.includes(context, 'paged') && useStructuredData) {
                    head.push('');
                    head.push.apply(head, finaliseStructuredData(metaData));
                    head.push('');

                    if (metaData.schema) {
                        head.push('<script type="application/ld+json">\n' +
                            JSON.stringify(metaData.schema, null, '    ') +
                            '\n    </script>\n');
                    }
                }

                if (!_.includes(context, 'amp') && labs.isSet('members')) {
                    head.push(getMembersHelper());
                }
            }

            head.push('<meta name="generator" content="Ghost ' +
                escapeExpression(safeVersion) + '" />');

            head.push('<link rel="alternate" type="application/rss+xml" title="' +
                escapeExpression(metaData.site.title) + '" href="' +
                escapeExpression(metaData.rssUrl) + '" />');

            // no code injection for amp context!!!
            if (!_.includes(context, 'amp')) {
                if (!_.isEmpty(globalCodeinjection)) {
                    head.push(globalCodeinjection);
                }

                if (!_.isEmpty(postCodeInjection)) {
                    head.push(postCodeInjection);
                }
            }
            debug('end');
            return new SafeString(head.join('\n    ').trim());
        })
        .catch(function handleError(err) {
            logging.error(err);

            // Return what we have so far (currently nothing)
            return new SafeString(head.join('\n    ').trim());
        });
};
<|endoftext|>


<commit_before>/*
 * remote.js: Commands related to working with remote configs
 *
 * (C) 2012, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    quill = require('../../quill');

var remote = exports;

remote.usage = [
  '`quill remote` commands allow you to work with remote configs',
  '',
  'quill remote create <name>',
  'quill remote list',
  'quill remote delete <name>',
  'quill remote view <name>',
  'quill remote get <name> <key>',
  'quill remote set <name> <key> <value>',
  'quill remote load <name> <file>'
];

//
// ### function create (name, callback)
// Creates a remote config called `name`.
//
remote.create = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Creating remote config: ' + result.name.magenta);
    quill.remote.create(result.name, callback);
  });
};

//
// ### function list (callback)
// Lists remote configs.
//
remote.list = function (callback) {
  quill.remote.list(function (err, remotes) {
    if (err) {
      return callback(err);
    }

    var rows = [['name']],
        colors = ['underline'];

    remotes.forEach(function (remote) {
      rows.push([remote.name]);
    });

    quill.inspect.putRows('data', rows, colors);
    callback();
  });
};

//
// ### function delete (name, callback)
// Delete `name` remote config.
//
remote.delete = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Deleting remote config: ' + result.name.magenta);
    quill.remote.destroy(result.name, callback);
  });
};

//
// ### function view (name, callback)
// View `name` remote config.
//
remote.view = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Viewing remote config: ' + result.name.magenta);
    quill.remote.get(result.name, function (err, remote) {
      if (err) {
        return callback(err);
      }

      quill.inspect.putObject(remote);
      callback();
    });
  });
};

//
// ### function get (name, key, callback)
// Get key `key` from remote config `name`.
//
remote.get = function (name, key, callback) {
  if (arguments.length !== 3) {
    return callback(new Error('Config name and key required.'));
  }

  quill.remote.get(name, function (err, remote) {
    if (err) {
      return callback(err);
    }

    quill.log.data(key.yellow + ' ' + remote.settings[key].magenta);
    callback();
  });
};

//
// ### function set (name, key, value, callback)
// Set key `key` in remote config `name` to `value`.
//
remote.set = function (name, key, value, callback) {
  if (arguments.length !== 4) {
    return callback(new Error('Config name, key and value required.'));
  }

  quill.remote.set(name, key, value, callback);
};

remote.load = function (name, file, callback) {
  if (arguments.length !== 3) {
    return callback(new Error('Config name and file required.'));
  }

  quill.log.info('Loading file ' + file.magenta + ' into ' + name.magenta + ' config');

  fs.readFile(file, function (err, data) {
    if (err) {
      return callback(err);
    }

    data = JSON.parse(data);
    quill.remote.destroy(name, function () {
      //
      // Ignore errors here.
      //
      quill.remote.create(name, data, callback);
    });
  });
};
<commit_msg>[api] Add `quill remote clear`<commit_after>/*
 * remote.js: Commands related to working with remote configs
 *
 * (C) 2012, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    quill = require('../../quill');

var remote = exports;

remote.usage = [
  '`quill remote` commands allow you to work with remote configs',
  '',
  'quill remote create <name>',
  'quill remote list',
  'quill remote delete <name>',
  'quill remote view <name>',
  'quill remote get <name> <key>',
  'quill remote set <name> <key> <value>',
  'quill remote clear <name> <key>',
  'quill remote load <name> <file>'
];

//
// ### function create (name, callback)
// Creates a remote config called `name`.
//
remote.create = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Creating remote config: ' + result.name.magenta);
    quill.remote.create(result.name, callback);
  });
};

//
// ### function list (callback)
// Lists remote configs.
//
remote.list = function (callback) {
  quill.remote.list(function (err, remotes) {
    if (err) {
      return callback(err);
    }

    var rows = [['name']],
        colors = ['underline'];

    remotes.forEach(function (remote) {
      rows.push([remote.name]);
    });

    quill.inspect.putRows('data', rows, colors);
    callback();
  });
};

//
// ### function delete (name, callback)
// Delete `name` remote config.
//
remote.delete = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Deleting remote config: ' + result.name.magenta);
    quill.remote.destroy(result.name, callback);
  });
};

//
// ### function view (name, callback)
// View `name` remote config.
//
remote.view = function (name, callback) {
  if (typeof name === 'string') {
    quill.prompt.override.name = name;
  }
  else {
    callback = name;
  }

  quill.prompt.get(['name'], function (err, result) {
    if (err) {
      return callback(err);
    }

    quill.log.info('Viewing remote config: ' + result.name.magenta);
    quill.remote.get(result.name, function (err, remote) {
      if (err) {
        return callback(err);
      }

      quill.inspect.putObject(remote);
      callback();
    });
  });
};

//
// ### function get (name, key, callback)
// Get key `key` from remote config `name`.
//
remote.get = function (name, key, callback) {
  if (arguments.length !== 3) {
    return callback(new Error('Config name and key required.'));
  }

  quill.remote.get(name, function (err, remote) {
    if (err) {
      return callback(err);
    }

    quill.log.data(key.yellow + ' ' + remote.settings[key].magenta);
    callback();
  });
};

//
// ### function set (name, key, value, callback)
// Set key `key` in remote config `name` to `value`.
//
remote.set = function (name, key, value, callback) {
  if (arguments.length !== 4) {
    return callback(new Error('Config name, key and value required.'));
  }

  quill.remote.set(name, key, value, callback);
};

//
// ### function clear (name, key, callback)
// Clear key `key` from remote config `name`.
//
remote.clear = function (name, key, callback) {
  if (arguments.length !== 3) {
    return callback(new Error('Config name and key required.'));
  }

  quill.remote.clear(name, key, callback);
};

remote.load = function (name, file, callback) {
  if (arguments.length !== 3) {
    return callback(new Error('Config name and file required.'));
  }

  quill.log.info('Loading file ' + file.magenta + ' into ' + name.magenta + ' config');

  fs.readFile(file, function (err, data) {
    if (err) {
      return callback(err);
    }

    data = JSON.parse(data);
    quill.remote.destroy(name, function () {
      //
      // Ignore errors here.
      //
      quill.remote.create(name, data, callback);
    });
  });
};
<|endoftext|>


<commit_before>/*
 * This file is part of the Spludo Framework.
 * Copyright (c) 2009-2010 DracoBlue, http://dracoblue.net/
 *
 * Licensed under the terms of MIT License. For the full copyright and license
 * information, please see the LICENSE file in the root folder.
 */

/**
 * @class The manager for all registered sessions.
 *
 * @extends Logging
 *
 * @since 0.1
 * @author DracoBlue
 */
CookieSessionManager = function(options) {
    options = options || {};

    if (typeof options.cookie_path !== "undefined") {
        this.cookie_path = options.cookie_path;
    } else {
        this.cookie_path = '/';
    }

    if (typeof options.cookie_key !== "undefined") {
        this.cookie_key = options.cookie_key;
    } else {
        this.cookie_key = "s";
    }

    var engine = GLOBAL[options.engine || "MemoryStorage"];

    this.storage = new engine("session_manager", options.engine_options || {});
};

extend(true, CookieSessionManager.prototype, Logging.prototype);

CookieSessionManager.prototype.logging_prefix = 'CookieSessionManager';

CookieSessionManager.prototype.removeSession = function(session_id) {
    var self = this;
    return function(cb) {
        self.info("removeSession: " + session_id);
        self.storage.remove(session_id)(function() {
            cb();
        });
    };
};

CookieSessionManager.prototype.getSession = function(session_id) {
    var self = this;
    return function(cb) {
        self.storage.get(session_id)(function(session_data) {
            if (!session_data) {
                cb(null);
            } else {
                self.log('getSession found', session_data);
                cb(JSON.parse(session_data));
            }
        });
    };
};

CookieSessionManager.prototype.setSession = function(session_id, session) {
    var self = this;
    return function(cb) {
        self.storage.set(session_id, JSON.stringify(session))(function() {
            cb();
        });
    };
};

CookieSessionManager.prototype.createSession = function(session) {
    var self = this;
    return function(cb) {
        var withUniqueSessionHandler = function(session_id) {
            self.info("createSession: " + session_id);
            self.storage.set(session_id, JSON.stringify(session))(function() {
                cb(session_id);
            });
        };

        /*
         * FIXME: This is an ugly solution. Guess what happens if we have lots
         * of session. We need some way better function here.
         */
        var tryToFindAnUnusedSessionId = function() {
            var session_id = new String(Math.floor(Math.random()*999999999));
            self.storage.has(session_id)(function(is_in_use) {
                if (is_in_use) {
                    session_id = null;
                    tryToFindAnUnusedSessionId();
                } else {
                    withUniqueSessionHandler(session_id);
                }
            });

        };

        tryToFindAnUnusedSessionId();
    };
};

CookieSessionManager.prototype.initializeWebContextSession = function (context, request) {
    var self = this;
    var session_id = (context.cookies && context.cookies[this.cookie_key]) || null;

    return function(cb) {
        if (session_id) {
            self.getSession(session_id)(function(session) {
                if (typeof session === 'undefined') {
                    /*
                     * Seems like that cookie is invalid by now. Let's remove the
                     * cookie.
                     */
                    ContextToolkit.removeCookie(context, self.cookie_key);
                    cb(null);
                } else {
                    context.session = session;
                    cb(session_id);
                }
            });
        } else {
            cb(null);
        }
    };
};

CookieSessionManager.prototype.finishWebContextSession = function (session_id, context, request) {
    var self = this;
    return function(cb) {
        if (session_id !== context.session_id) {
            session_id = context.session_id;

            if (session_id === null) {
                ContextToolkit.removeCookie(context, self.cookie_key);
            } else {
                ContextToolkit.setCookie(context, self.cookie_key, session_id, 0, self.cookie_path);
            }
        }
        cb();
    };
};
<commit_msg>Fixed logging für the CookieSessionManager<commit_after>/*
 * This file is part of the Spludo Framework.
 * Copyright (c) 2009-2010 DracoBlue, http://dracoblue.net/
 *
 * Licensed under the terms of MIT License. For the full copyright and license
 * information, please see the LICENSE file in the root folder.
 */

/**
 * @class The manager for all registered sessions.
 *
 * @extends Logging
 *
 * @since 0.1
 * @author DracoBlue
 */
CookieSessionManager = function(options) {
    options = options || {};

    if (typeof options.cookie_path !== "undefined") {
        this.cookie_path = options.cookie_path;
    } else {
        this.cookie_path = '/';
    }

    if (typeof options.cookie_key !== "undefined") {
        this.cookie_key = options.cookie_key;
    } else {
        this.cookie_key = "s";
    }

    var engine = GLOBAL[options.engine || "MemoryStorage"];

    this.storage = new engine("session_manager", options.engine_options || {});
};

extend(true, CookieSessionManager.prototype, Logging.prototype);

CookieSessionManager.prototype.logging_prefix = 'CookieSessionManager';

CookieSessionManager.prototype.removeSession = function(session_id) {
    var self = this;
    self.trace("removeSession", arguments);
    return function(cb) {
        self.storage.remove(session_id)(function() {
            cb();
        });
    };
};

CookieSessionManager.prototype.getSession = function(session_id) {
    var self = this;
    return function(cb) {
        self.storage.get(session_id)(function(session_data) {
            if (!session_data) {
                cb(null);
            } else {
                self.log('getSession found', session_data);
                cb(JSON.parse(session_data));
            }
        });
    };
};

CookieSessionManager.prototype.setSession = function(session_id, session) {
    var self = this;
    return function(cb) {
        self.storage.set(session_id, JSON.stringify(session))(function() {
            cb();
        });
    };
};

CookieSessionManager.prototype.createSession = function(session) {
    var self = this;
    self.trace("createSession", arguments);
    return function(cb) {
        var withUniqueSessionHandler = function(session_id) {
            self.storage.set(session_id, JSON.stringify(session))(function() {
                cb(session_id);
            });
        };

        /*
         * FIXME: This is an ugly solution. Guess what happens if we have lots
         * of session. We need some way better function here.
         */
        var tryToFindAnUnusedSessionId = function() {
            var session_id = new String(Math.floor(Math.random()*999999999));
            self.storage.has(session_id)(function(is_in_use) {
                if (is_in_use) {
                    session_id = null;
                    tryToFindAnUnusedSessionId();
                } else {
                    withUniqueSessionHandler(session_id);
                }
            });

        };

        tryToFindAnUnusedSessionId();
    };
};

CookieSessionManager.prototype.initializeWebContextSession = function (context, request) {
    var self = this;
    var session_id = (context.cookies && context.cookies[this.cookie_key]) || null;

    return function(cb) {
        if (session_id) {
            self.getSession(session_id)(function(session) {
                if (typeof session === 'undefined') {
                    /*
                     * Seems like that cookie is invalid by now. Let's remove the
                     * cookie.
                     */
                    ContextToolkit.removeCookie(context, self.cookie_key);
                    cb(null);
                } else {
                    context.session = session;
                    cb(session_id);
                }
            });
        } else {
            cb(null);
        }
    };
};

CookieSessionManager.prototype.finishWebContextSession = function (session_id, context, request) {
    var self = this;
    return function(cb) {
        if (session_id !== context.session_id) {
            session_id = context.session_id;

            if (session_id === null) {
                ContextToolkit.removeCookie(context, self.cookie_key);
            } else {
                ContextToolkit.setCookie(context, self.cookie_key, session_id, 0, self.cookie_path);
            }
        }
        cb();
    };
};
<|endoftext|>


<commit_before>var firebase = require('firebase');

var return_url = process.env.APP_BASE_URL + process.env.APP_PAYPAL_SUCCESS_CALLBACK;
var cancel_url = process.env.APP_BASE_URL + process.env.APP_PAYPAL_CANCEL_CALLBACK;

//create a firebase connection
var firebaseRef = new firebase(process.env.FIREBASE_URL);
//authenticate with firebase server
firebaseRef.authWithCustomToken(process.env.FIREBASE_TOKEN, function(error, authData){
    if(error){
        throw "Firebase Auth Failed for server!";
    }
});

//model object
module.exports = {
    'firebase': firebaseRef,
    'plans': {
        //defines the plans that are available
        "2999": {
            "description": "Regular Plan",
            "merchant_preferences": {
                "auto_bill_amount": "yes",
                "cancel_url": cancel_url,
                "initial_fail_amount_action": "continue",
                "max_fail_attempts": "1",
                "return_url": return_url,
                "setup_fee": {
                    "currency": "SGD",
                    "value": "0"
                }
            },
            "name": "Regular Plan",
            "payment_definitions": [
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "29.99"
                    },
                    "cycles": "0",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Regular 1",
                    "type": "REGULAR"
                },
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "19.99"
                    },
                    "cycles": "1",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Trial 1",
                    "type": "TRIAL"
                }
            ],
            "type": "INFINITE"
        },
        '5999': {
            "description": "Premium Plan",
            "merchant_preferences": {
                "auto_bill_amount": "yes",
                "cancel_url": cancel_url,
                "initial_fail_amount_action": "continue",
                "max_fail_attempts": "1",
                "return_url": return_url,
                "setup_fee": {
                    "currency": "SGD",
                    "value": "0"
                }
            },
            "name": "Premium Plan",
            "payment_definitions": [
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "59.99"
                    },
                    "cycles": "0",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Premium 1",
                    "type": "REGULAR"
                },
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "39.99"
                    },
                    "cycles": "1",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Trial 2",
                    "type": "TRIAL"
                }
            ],
            "type": "INFINITE"
        }
    },
    //defines the data required to activate the plan
    'activatePlan':[{
        "op": "replace",
        "path": '/',
        "value": {
            "state": "ACTIVE"
        }
    }],
    //creates billing agreement data based on the tier and address
    'createAgreementData': function(tier, planId, address){
        return {
            "name": tier == '2999'? "Regular Plan": "Premium Plan",
            "description": tier == '2999'? "Regular Plan": "Premium Plan",
            "start_date": getStartDate(),
            "plan":{
                "id": planId
            },
            "payer": {
                "payment_method": "paypal"
            },
            "shipping_address":{
                "line1": address["line1"] ? address["line1"]:"",
                "line2": address["line2"] ? address["line2"]:"",
                "city": address["city"] ? address["city"]:"",
                "state": address["state"] ? address["state"]:"",
                "postal_code": address["postal_code"] ? address["postal_code"]:"",
                "country_code": address["country_code"] ? address["country_code"]:""
            }
        }
    },
    //sample address
    'address':{
        'line1': '1 Temasek Avenue',
        'line2': '#14-01',
        'city': 'Singapore',
        'state': 'Singapore',
        'postal_code': '039192',
        'country_code': 'SG'
    }
}

//utlity functions to make generating the start date above easier
function PadZeros(value, desiredStringLength){
    var num = value + "";
    while (num.length < desiredStringLength){
        num = "0" + num;
    }
    return num;
}
function toIsoString(d){
    return d.getUTCFullYear() + '-' + PadZeros(d.getUTCMonth() + 1, 2) + '-' + PadZeros(d.getUTCDate(), 2) + 'T' + PadZeros(d.getUTCHours(), 2) + ':' + PadZeros(d.getUTCMinutes(), 2) + ':' + PadZeros(d.getUTCSeconds(), 2) + 'Z';
}

function getStartDate(){
    var start_date = new Date();
    start_date.setMinutes(start_date.getMinutes() + 5);
    return toIsoString(start_date);
}<commit_msg>lol<commit_after>var firebase = require('firebase');

var return_url = process.env.APP_BASE_URL + process.env.APP_PAYPAL_SUCCESS_CALLBACK;
var cancel_url = process.env.APP_BASE_URL + process.env.APP_PAYPAL_CANCEL_CALLBACK;

//create a firebase connection
var firebaseRef = new firebase(process.env.FIREBASE_URL);
//authenticate with firebase server
firebaseRef.authWithCustomToken(process.env.FIREBASE_TOKEN, function(error, authData){
    if(error){
        throw "Firebase Auth Failed for server!";
    }
});

//model object
module.exports = {
    'firebase': firebaseRef,
    'plans': {
        //defines the plans that are available
        "2999": {
            "description": "Regular Plan",
            "merchant_preferences": {
                "auto_bill_amount": "yes",
                "cancel_url": cancel_url,
                "initial_fail_amount_action": "continue",
                "max_fail_attempts": "1",
                "return_url": return_url,
                "setup_fee": {
                    "currency": "SGD",
                    "value": "0"
                }
            },
            "name": "Regular Plan",
            "payment_definitions": [
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "0.01"
                    },
                    "cycles": "0",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Regular 1",
                    "type": "REGULAR"
                },
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "0.01"
                    },
                    "cycles": "1",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Trial 1",
                    "type": "TRIAL"
                }
            ],
            "type": "INFINITE"
        },
        '5999': {
            "description": "Premium Plan",
            "merchant_preferences": {
                "auto_bill_amount": "yes",
                "cancel_url": cancel_url,
                "initial_fail_amount_action": "continue",
                "max_fail_attempts": "1",
                "return_url": return_url,
                "setup_fee": {
                    "currency": "SGD",
                    "value": "0"
                }
            },
            "name": "Premium Plan",
            "payment_definitions": [
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "0.01"
                    },
                    "cycles": "0",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Premium 1",
                    "type": "REGULAR"
                },
                {
                    "amount": {
                        "currency": "SGD",
                        "value": "0.01"
                    },
                    "cycles": "1",
                    "frequency": "MONTH",
                    "frequency_interval": "1",
                    "name": "Trial 2",
                    "type": "TRIAL"
                }
            ],
            "type": "INFINITE"
        }
    },
    //defines the data required to activate the plan
    'activatePlan':[{
        "op": "replace",
        "path": '/',
        "value": {
            "state": "ACTIVE"
        }
    }],
    //creates billing agreement data based on the tier and address
    'createAgreementData': function(tier, planId, address){
        return {
            "name": tier == '2999'? "Regular Plan": "Premium Plan",
            "description": tier == '2999'? "Regular Plan": "Premium Plan",
            "start_date": getStartDate(),
            "plan":{
                "id": planId
            },
            "payer": {
                "payment_method": "paypal"
            },
            "shipping_address":{
                "line1": address["line1"] ? address["line1"]:"",
                "line2": address["line2"] ? address["line2"]:"",
                "city": address["city"] ? address["city"]:"",
                "state": address["state"] ? address["state"]:"",
                "postal_code": address["postal_code"] ? address["postal_code"]:"",
                "country_code": address["country_code"] ? address["country_code"]:""
            }
        }
    },
    //sample address
    'address':{
        'line1': '1 Temasek Avenue',
        'line2': '#14-01',
        'city': 'Singapore',
        'state': 'Singapore',
        'postal_code': '039192',
        'country_code': 'SG'
    }
}

//utlity functions to make generating the start date above easier
function PadZeros(value, desiredStringLength){
    var num = value + "";
    while (num.length < desiredStringLength){
        num = "0" + num;
    }
    return num;
}
function toIsoString(d){
    return d.getUTCFullYear() + '-' + PadZeros(d.getUTCMonth() + 1, 2) + '-' + PadZeros(d.getUTCDate(), 2) + 'T' + PadZeros(d.getUTCHours(), 2) + ':' + PadZeros(d.getUTCMinutes(), 2) + ':' + PadZeros(d.getUTCSeconds(), 2) + 'Z';
}

function getStartDate(){
    var start_date = new Date();
    start_date.setMinutes(start_date.getMinutes() + 5);
    return toIsoString(start_date);
}<|endoftext|>


<commit_before>const debug = require('ghost-ignition').debug('web:admin:controller');
const path = require('path');
const config = require('../../../shared/config');
const updateCheck = require('../../update-check');
const logging = require('../../../shared/logging');

/**
 * @description Admin controller to handle /ghost/ requests.
 *
 * Every request to the admin panel will re-trigger the update check service.
 *
 * @param req
 * @param res
 */
module.exports = function adminController(req, res) {
    debug('index called');

    // CASE: trigger update check unit and let it run in background, don't block the admin rendering
    updateCheck()
        .catch((err) => {
            logging.error(err);
        });

    const defaultTemplate = config.get('env') === 'production' ? 'default-prod.html' : 'default.html';
    const templatePath = path.resolve(config.get('paths').adminViews, defaultTemplate);
    const headers = {};

    if (config.get('adminFrameProtection')) {
        headers['X-Frame-Options'] = 'sameorigin';
    }

    res.sendFile(templatePath, {headers});
};
<commit_msg>Removed duplicate update check error  hanlindg<commit_after>const debug = require('ghost-ignition').debug('web:admin:controller');
const path = require('path');
const config = require('../../../shared/config');
const updateCheck = require('../../update-check');

/**
 * @description Admin controller to handle /ghost/ requests.
 *
 * Every request to the admin panel will re-trigger the update check service.
 *
 * @param req
 * @param res
 */
module.exports = function adminController(req, res) {
    debug('index called');

    // CASE: trigger update check unit and let it run in background, don't block the admin rendering
    updateCheck();

    const defaultTemplate = config.get('env') === 'production' ? 'default-prod.html' : 'default.html';
    const templatePath = path.resolve(config.get('paths').adminViews, defaultTemplate);
    const headers = {};

    if (config.get('adminFrameProtection')) {
        headers['X-Frame-Options'] = 'sameorigin';
    }

    res.sendFile(templatePath, {headers});
};
<|endoftext|>


<commit_before>var the = {},

	substance = exports.substance = function(name){
		console.log('construct substance', name);
		var it = this;
		it.name = name;
		the[name] = exports[name] = it;
		it.pour = function(mix, volume){
			return mix.add(it, volume);
		};
		it.toString = function(){
			return '#' + it.name.toUpperCase();
		};
	},

	water = substance('water'),
	alcohol = substance('alcohol'),

	mix = exports.mix = function(){
		var schema = [];
		console.log('arguments', arguments);
		for(var index = 0, limit = arguments.length; index < limit; index++){
			var name = arguments[index];
			schema[index] = the[name];
			console.log(name, the[name]);
		}
		console.log('schema', schema);
		return function(){
			console.log('construct mix');
			var content = this.content = {},
				reset = function(ingredient){
					if(typeof ingredient === 'undefined'){
						throw 'undefined ingredient';
					}
					var key = (typeof ingredient === 'String') ? ingredient : ingredient.name;
					content[key] = 0;
				};
			for(var index = 0, limit = schema.length; index < limit; index++){
				try{
					var ingredient = schema[index];
					console.log('ingredient', index, ingredient);
					reset(ingredient);
				}catch(condition){
					throw condition + ' in ' + schema + '[' + index + ']';
				}
			}
			var pick = this.ingredient = function(name){
				return (typeof content[key] !== 'undefined') && content[key];
			};
			this.add = function(it, volume){
				if(volume < 0){
					throw 'can not add negative volume of ' + it;
				}
				var key = it.name,
					ingredient = pick(name);
				if(!ingredient){
					reset(key);
				}
				return content[key] += volume;
			};
		};
	},

	schnapps = exports.schnapps = mix(water, alcohol);

mix.prototype.volume = function(){
	var content = this.content,
		total = 0;
	for(var name in content){
		var volume = content[name];
		total += volume;
	}
	return total;
};

schnapps.prototype.strength = function(){
	if(!this.content.water){
		return this.content.alcohol ? 1 : 0;
	}
	return this.content.alcohol / this.content.water;
};

schnapps.prototype.toString = function(){
	return 'Mix :: '
		+ 'Water = ' + this.content.water + ', '
		+ 'Alcohol = ' + this.content.alcohol + ', '
		+ 'Volume = ' + this.volume() + ', '
		+ 'Strength = ' + this.strength() + '.';
};
<commit_msg>fix. @todo: fix arguments<commit_after>var the = {},

	substance = exports.substance = function(name){
		console.log('construct substance', name);
		var it = this;
		it.name = name;
		the[name] = exports[name] = it;
		it.pour = function(mix, volume){
			return mix.add(it, volume);
		};
		it.toString = function(){
			return '#' + it.name.toUpperCase();
		};
	},

	water = substance('water'),
	alcohol = substance('alcohol'),

	mix = exports.mix = function(){
		var schema = [];
		console.log('arguments', arguments);
		for(var index = 0, limit = arguments.length; index < limit; index++){
			var name = arguments[index];
			schema[index] = the[name];
			console.log(name, the[name]);
		}
		console.log('schema', schema);
		return function(){
			console.log('construct mix');
			var content = this.content = {},
				reset = function(ingredient){
					if(typeof ingredient === 'undefined'){
						throw 'undefined ingredient';
					}
					var key = (typeof ingredient === 'String') ? ingredient : ingredient.name;
					content[key] = 0;
				};
			for(var index = 0, limit = schema.length; index < limit; index++){
				try{
					var ingredient = schema[index];
					console.log('ingredient', index, ingredient);
					reset(ingredient);
				}catch(condition){
					throw condition + ' in ' + schema + '[' + index + ']';
				}
			}
			var pick = this.ingredient = function(name){
				return (typeof content[name] !== 'undefined') && content[name];
			};
			this.add = function(it, volume){
				if(volume < 0){
					throw 'can not add negative volume of ' + it;
				}
				var key = it.name,
					ingredient = pick(name);
				if(!ingredient){
					reset(key);
				}
				return content[key] += volume;
			};
		};
	},

	schnapps = exports.schnapps = mix(water, alcohol);

mix.prototype.volume = function(){
	var content = this.content,
		total = 0;
	for(var name in content){
		var volume = content[name];
		total += volume;
	}
	return total;
};

schnapps.prototype.strength = function(){
	if(!this.content.water){
		return this.content.alcohol ? 1 : 0;
	}
	return this.content.alcohol / this.content.water;
};

schnapps.prototype.toString = function(){
	return 'Mix :: '
		+ 'Water = ' + this.content.water + ', '
		+ 'Alcohol = ' + this.content.alcohol + ', '
		+ 'Volume = ' + this.volume() + ', '
		+ 'Strength = ' + this.strength() + '.';
};
<|endoftext|>


<commit_before>var Reaction = require("../reaction")
  , rules = require("../rules")
  , _ = require("underscore")

var ConditionalReaction = Reaction.extend({

  init: function(next) {
    this.setConditional()
    next()
  },

  setConditional: function() {
    var shown = _(this.conditionals).every(function(isNegation, conditionalName) {
      //xor(^) in place of && + ||
      return isNegation ^ !!this.get(conditionalName)
    }, this)

    if(shown) {
      this.el.show()
    } else {
      this.el.hide()
    }
  },

  observe: function() {
    var events = _.keys(this.conditionals).join(" ")
    this.change(events, function() {
      this.setConditional()
    }, this)
  }
}, {
  selector: "[class]",

  reactIf: function(el) {
    return rules.conditional(el)
  },

  parse: function(el) {
    return {
      conditionals: rules.conditionals(el),
    }
  }
})

module.exports = ConditionalReaction
<commit_msg>remove trailing , as that breaks ie7<commit_after>var Reaction = require("../reaction")
  , rules = require("../rules")
  , _ = require("underscore")

var ConditionalReaction = Reaction.extend({

  init: function(next) {
    this.setConditional()
    next()
  },

  setConditional: function() {
    var shown = _(this.conditionals).every(function(isNegation, conditionalName) {
      //xor(^) in place of && + ||
      return isNegation ^ !!this.get(conditionalName)
    }, this)

    if(shown) {
      this.el.show()
    } else {
      this.el.hide()
    }
  },

  observe: function() {
    var events = _.keys(this.conditionals).join(" ")
    this.change(events, function() {
      this.setConditional()
    }, this)
  }
}, {
  selector: "[class]",

  reactIf: function(el) {
    return rules.conditional(el)
  },

  parse: function(el) {
    return {
      conditionals: rules.conditionals(el)
    }
  }
})

module.exports = ConditionalReaction
<|endoftext|>


<commit_before>module('Dropdown - attachBody - positioning');

test('appends to the dropdown parent', function (assert) {
    assert.expect(4);

    var $ = require('jquery');

    var $select = $('<select></select>');
    var $parent = $('

');

    var $container = $('');
    var container = new MockContainer();

    $parent.appendTo($('#qunit-fixture'));
    $select.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    assert.equal(
        $parent.children().length,
        1,
        'Only the select should be in the container'
    );

    var $dropdown = dropdown.render();

    dropdown.bind(container, $container);

    dropdown.position($dropdown, $container);

    assert.equal(
        $parent.children().length,
        1,
        'The dropdown should not be placed until after it is opened'
    );

    dropdown._showDropdown();

    assert.equal(
        $parent.children().length,
        2,
        'The dropdown should now be in the container as well'
    );

    assert.ok(
        $.contains($parent[0], $dropdown[0]),
        'The dropdown should be contained within the parent container'
    );
});

test('dropdown is positioned down with static margins', function (assert) {
    var $ = require('jquery');
    var $select = $('<select></select>');
    var $parent = $('

');
    $parent.css({
        position: 'static',
        marginTop: '5px',
        marginLeft: '10px'
    });

    var $container = $('test');
    var container = new MockContainer();

    $('#qunit-fixture').empty();

    $parent.appendTo($('#qunit-fixture'));
    $container.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    var $dropdown = dropdown.render();

    assert.equal(
        $dropdown[0].style.top,
        0,
        'The drodpown should not have any offset before it is displayed'
    );

    dropdown.bind(container, $container);
    dropdown.position($dropdown, $container);
    dropdown._showDropdown();

    assert.ok(
        dropdown.$dropdown.hasClass('select2-dropdown--below'),
        'The dropdown should be forced down'
    );

    assert.equal(
        $dropdown.css('top').replace(/\D+/, ''),
        $container.outerHeight() + 5,
        'The offset should be 5px at the top'
    );

    assert.equal(
        $dropdown.css('left'),
        '10px',
        'The offset should be 10px on the left'
    );
});

test('dropdown is positioned down with absolute offsets', function (assert) {
    var $ = require('jquery');
    var $select = $('<select></select>');
    var $parent = $('

');
    $parent.css({
        position: 'absolute',
        top: '10px',
        left: '5px'
    });

    var $container = $('test');
    var container = new MockContainer();

    $parent.appendTo($('#qunit-fixture'));
    $container.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    var $dropdown = dropdown.render();

    assert.equal(
        $dropdown[0].style.top,
        0,
        'The drodpown should not have any offset before it is displayed'
    );

    dropdown.bind(container, $container);
    dropdown.position($dropdown, $container);
    dropdown._showDropdown();

    assert.ok(
        dropdown.$dropdown.hasClass('select2-dropdown--below'),
        'The dropdown should be forced down'
    );

    assert.equal(
        $dropdown.css('top').replace(/\D+/, ''),
        $container.outerHeight(),
        'There should not be an extra top offset'
    );

    assert.equal(
        $dropdown.css('left'),
        '0px',
        'There should not be an extra left offset'
    );
});<commit_msg>Add tests for when dropdownParent isn't in the document<commit_after>module('Dropdown - attachBody - positioning');

test('appends to the dropdown parent', function (assert) {
    assert.expect(4);

    var $ = require('jquery');

    var $select = $('<select></select>');
    var $parent = $('

');

    var $container = $('');
    var container = new MockContainer();

    $parent.appendTo($('#qunit-fixture'));
    $select.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    assert.equal(
        $parent.children().length,
        1,
        'Only the select should be in the container'
    );

    var $dropdown = dropdown.render();

    dropdown.bind(container, $container);

    dropdown.position($dropdown, $container);

    assert.equal(
        $parent.children().length,
        1,
        'The dropdown should not be placed until after it is opened'
    );

    dropdown._showDropdown();

    assert.equal(
        $parent.children().length,
        2,
        'The dropdown should now be in the container as well'
    );

    assert.ok(
        $.contains($parent[0], $dropdown[0]),
        'The dropdown should be contained within the parent container'
    );
});

test('dropdown is positioned down with static margins', function (assert) {
    var $ = require('jquery');
    var $select = $('<select></select>');
    var $parent = $('

');
    $parent.css({
        position: 'static',
        marginTop: '5px',
        marginLeft: '10px'
    });

    var $container = $('test');
    var container = new MockContainer();

    $('#qunit-fixture').empty();

    $parent.appendTo($('#qunit-fixture'));
    $container.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    var $dropdown = dropdown.render();

    assert.equal(
        $dropdown[0].style.top,
        0,
        'The drodpown should not have any offset before it is displayed'
    );

    dropdown.bind(container, $container);
    dropdown.position($dropdown, $container);
    dropdown._showDropdown();

    assert.ok(
        dropdown.$dropdown.hasClass('select2-dropdown--below'),
        'The dropdown should be forced down'
    );

    assert.equal(
        $dropdown.css('top').replace(/\D+/, ''),
        $container.outerHeight() + 5,
        'The offset should be 5px at the top'
    );

    assert.equal(
        $dropdown.css('left'),
        '10px',
        'The offset should be 10px on the left'
    );
});

test('dropdown is positioned down with absolute offsets', function (assert) {
    var $ = require('jquery');
    var $select = $('<select></select>');
    var $parent = $('

');
    $parent.css({
        position: 'absolute',
        top: '10px',
        left: '5px'
    });

    var $container = $('test');
    var container = new MockContainer();

    $parent.appendTo($('#qunit-fixture'));
    $container.appendTo($parent);

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $parent
    }));

    var $dropdown = dropdown.render();

    assert.equal(
        $dropdown[0].style.top,
        0,
        'The drodpown should not have any offset before it is displayed'
    );

    dropdown.bind(container, $container);
    dropdown.position($dropdown, $container);
    dropdown._showDropdown();

    assert.ok(
        dropdown.$dropdown.hasClass('select2-dropdown--below'),
        'The dropdown should be forced down'
    );

    assert.equal(
        $dropdown.css('top').replace(/\D+/, ''),
        $container.outerHeight(),
        'There should not be an extra top offset'
    );

    assert.equal(
        $dropdown.css('left'),
        '0px',
        'There should not be an extra left offset'
    );
});

test('dropdown is positioned even when not in document', function (assert) {
    var $ = require('jquery');
    var $select = $('<select></select>');

    var $container = $('test');
    var container = new MockContainer();

    var Utils = require('select2/utils');
    var Options = require('select2/options');

    var Dropdown = require('select2/dropdown');
    var AttachBody = require('select2/dropdown/attachBody');

    var DropdownAdapter = Utils.Decorate(Dropdown, AttachBody);

    var dropdown = new DropdownAdapter($select, new Options({
        dropdownParent: $('html')
    }));

    var $dropdown = dropdown.render();

    assert.equal(
        $dropdown[0].style.top,
        0,
        'The drodpown should not have any offset before it is displayed'
    );

    dropdown.bind(container, $container);
    dropdown.position($dropdown, $container);
    dropdown._showDropdown();

    assert.ok(
        dropdown.$dropdown.hasClass('select2-dropdown--below'),
        'The dropdown should be forced down'
    );

    assert.equal(
        $dropdown.css('top'),
        '0px',
        'The offset should be 0px at the top'
    );

    assert.equal(
        $dropdown.css('left'),
        '0px',
        'The offset should be 0px on the left'
    );
});
<|endoftext|>


<commit_before>/**
 * jspsych-webgazer-validate
 * Josh de Leeuw
 **/

jsPsych.plugins["webgazer-validate"] = (function() {

    var plugin = {};

    plugin.info = {
      name: 'webgazer-validate',
      description: '',
      parameters: {
        validation_points: {
          type: jsPsych.plugins.parameterType.INT,
          default: [[10,10], [10,50], [10,90], [50,10], [50,50], [50,90], [90,10], [90,50], [90,90]]
        },
        validation_point_coordinates: {
          type: jsPsych.plugins.parameterType.STRING,
          default: 'percent' // options: 'percent', 'center-offset-pixels'
        },
        roi_radius: {
          type: jsPsych.plugins.parameterType.INT,
          default: 200
        },
        randomize_validation_order: {
          type: jsPsych.plugins.parameterType.BOOL,
          default: false
        },
        validation_duration: {
          type: jsPsych.plugins.parameterType.INT,
          default: 2000
        },
        point_size:{
          type: jsPsych.plugins.parameterType.INT,
          default: 10
        },
        show_validation_data: {
          type: jsPsych.plugins.parameterType.BOOL,
          default: false
        }
      }
    }

    plugin.trial = function(display_element, trial) {

      var trial_data = {}
      trial_data.raw_gaze = [];
      trial_data.percent_in_roi = [];
      trial_data.average_offset = [];

      var html = `

`

      display_element.innerHTML = html;

      var wg_container = display_element.querySelector('#webgazer-validate-container');

      show_begin_validate_message();

      function show_begin_validate_message(){
        wg_container.innerHTML = `

Let's see how accurate the eye tracking is.

Keep your head still, and move your eyes to focus on each dot as it appears.

          <button id='begin-validate-btn' class='jspsych-btn'>Click to begin.</button>

`
        document.querySelector('#begin-validate-btn').addEventListener('click', function(){
          validate();
        });
      }

      var points_completed = -1;
      var val_points = null;

      function validate(){

        if(trial.randomize_validation_order){
          val_points = jsPsych.randomization.shuffle(trial.validation_points);
        } else {
          val_points = trial.validation_points;
        }
        points_completed = -1;
        jsPsych.extensions['webgazer'].resume();
        jsPsych.extensions.webgazer.showPredictions();
        next_validation_point();
      }

      function next_validation_point(){
        points_completed++;
        if(points_completed == val_points.length){
          validation_done();
        } else {
          var pt = val_points[points_completed];
          validation_display(pt);
        }
      }

      function validation_display(pt){
        var pt_html = drawValidationPoint(pt[0], pt[1]);
        wg_container.innerHTML = pt_html;

        var pt_dom = wg_container.querySelector('.validation-point');

        var br = pt_dom.getBoundingClientRect();
        var x = br.left + br.width / 2;
        var y = br.top + br.height / 2;

        var pt_start_val = performance.now() + 1000;
        var pt_finish = performance.now() + 3000;

        var pt_data = [];

        requestAnimationFrame(function watch_dot(){

          if(performance.now() > pt_start_val){
            jsPsych.extensions['webgazer'].getCurrentPrediction().then(function(prediction){
              pt_data.push({dx: prediction.x - x, dy: prediction.y - y});
            });
          }
          if(performance.now() < pt_finish){
            requestAnimationFrame(watch_dot);
          } else {
            trial_data.raw_gaze.push(pt_data);
            next_validation_point();
          }
        });

      }

      function drawValidationPoint(x,y){
        if(trial.validation_point_coordinates == 'percent'){
          return drawValidationPoint_PercentMode(x,y);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawValidationPoint_CenterOffsetMode(x,y);
        }
      }

      function drawValidationPoint_PercentMode(x,y){
        return `

`
      }

      function drawValidationPoint_CenterOffsetMode(x,y){
        return `

`
      }

      function drawCircle(target_x, target_y, dx, dy, r){
        if(trial.validation_point_coordinates == 'percent'){
          return drawCircle_PercentMode(target_x, target_y, dx, dy, r);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r);
        }
      }

      function drawCircle_PercentMode(target_x, target_y, dx, dy, r){
        var html = `

        `
        return html;
      }

      function drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r){
        var html = `

        `
        return html;
      }

      function drawRawDataPoint(target_x, target_y, dx, dy, ){
        if(trial.validation_point_coordinates == 'percent'){
          return drawRawDataPoint_PercentMode(target_x, target_y, dx, dy);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy);
        }
      }

      function drawRawDataPoint_PercentMode(target_x, target_y, dx, dy){
        var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
        return `

`
      }

      function drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy){
        var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
        return `

`
      }

      function median(arr){
        var mid = Math.floor(arr.length/2);
        var sorted_arr = arr.sort((a,b) => a-b);
        if(arr.length % 2 == 0){
          return sorted_arr[mid-1] + sorted_arr[mid] / 2;
        } else {
          return sorted_arr[mid];
        }
      }

      function calculateGazeCentroid(gazeData){

        var x_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
          accumulator += currentValue.dx;
          if(index == gazeData.length-1){
            return accumulator / gazeData.length;
          } else {
            return accumulator;
          }
        }, 0);

        var y_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
          accumulator += currentValue.dy;
          if(index == gazeData.length-1){
            return accumulator / gazeData.length;
          } else {
            return accumulator;
          }
        }, 0);

        var median_distance = median(gazeData.map(function(x){ return(Math.sqrt(Math.pow(x.dx-x_diff_m,2) + Math.pow(x.dy-y_diff_m,2)))}));

        return {
          x: x_diff_m,
          y: y_diff_m,
          r: median_distance
        }
      }

      function calculatePercentInROI(gazeData){
        var distances = gazeData.map(function(p){
          return(Math.sqrt(Math.pow(p.dx,2) + Math.pow(p.dy,2)))
        });
        var sum_in_roi = distances.reduce(function(accumulator, currentValue){
          if(currentValue <= trial.roi_radius){
            accumulator++;
          }
          return accumulator;
        }, 0);
        var percent = sum_in_roi / gazeData.length * 100;
        return percent;
      }

      function validation_done(){
        for(var i=0; i<trial.validation_points.length; i++){
          trial_data.percent_in_roi[i] = calculatePercentInROI(trial_data.raw_gaze[i]);
          trial_data.average_offset[i] = calculateGazeCentroid(trial_data.raw_gaze[i]);
        }
        if(trial.show_validation_data){
          show_validation_data();
        } else {
          end_trial();
        }
      }

      function show_validation_data(){
        var html = '';
        for(var i=0; i<trial.validation_points.length; i++){
          html += drawValidationPoint(trial.validation_points[i][0], trial.validation_points[i][1]);
          html += drawCircle(trial.validation_points[i][0], trial.validation_points[i][1], 0, 0, trial.roi_radius);
          for(var j=0; j<trial_data.raw_gaze[i].length; j++){
            html += drawRawDataPoint(trial.validation_points[i][0], trial.validation_points[i][1], trial_data.raw_gaze[i][j].dx, trial_data.raw_gaze[i][j].dy)
          }
        }
        // debugging
        html += '<button id="cont" style="position:absolute; top: 50%; left:calc(50% - 50px); width: 100px;" class="jspsych-btn">Continue</btn>';
        wg_container.innerHTML = html;
        wg_container.querySelector('#cont').addEventListener('click', end_trial);
        jsPsych.extensions.webgazer.showPredictions();
      }

      // function to end trial when it is time
      function end_trial() {
        jsPsych.extensions['webgazer'].pause();
        jsPsych.extensions['webgazer'].hidePredictions();
        jsPsych.extensions['webgazer'].hideVideo();

        // kill any remaining setTimeout handlers
        jsPsych.pluginAPI.clearAllTimeouts();

        // clear the display
        display_element.innerHTML = '';

        // move on to the next trial
        jsPsych.finishTrial(trial_data);
      };

    };

    return plugin;
  })();<commit_msg>add timing params<commit_after>/**
 * jspsych-webgazer-validate
 * Josh de Leeuw
 **/

jsPsych.plugins["webgazer-validate"] = (function() {

    var plugin = {};

    plugin.info = {
      name: 'webgazer-validate',
      description: '',
      parameters: {
        validation_points: {
          type: jsPsych.plugins.parameterType.INT,
          default: [[10,10], [10,50], [10,90], [50,10], [50,50], [50,90], [90,10], [90,50], [90,90]]
        },
        validation_point_coordinates: {
          type: jsPsych.plugins.parameterType.STRING,
          default: 'percent' // options: 'percent', 'center-offset-pixels'
        },
        roi_radius: {
          type: jsPsych.plugins.parameterType.INT,
          default: 200
        },
        randomize_validation_order: {
          type: jsPsych.plugins.parameterType.BOOL,
          default: false
        },
        time_to_saccade: {
          type: jsPsych.plugins.parameterType.INT,
          default: 1000
        },
        validation_duration: {
          type: jsPsych.plugins.parameterType.INT,
          default: 2000
        },
        point_size:{
          type: jsPsych.plugins.parameterType.INT,
          default: 10
        },
        show_validation_data: {
          type: jsPsych.plugins.parameterType.BOOL,
          default: false
        }
      }
    }

    plugin.trial = function(display_element, trial) {

      var trial_data = {}
      trial_data.raw_gaze = [];
      trial_data.percent_in_roi = [];
      trial_data.average_offset = [];

      var html = `

`

      display_element.innerHTML = html;

      var wg_container = display_element.querySelector('#webgazer-validate-container');

      show_begin_validate_message();

      function show_begin_validate_message(){
        wg_container.innerHTML = `

Let's see how accurate the eye tracking is.

Keep your head still, and move your eyes to focus on each dot as it appears.

          <button id='begin-validate-btn' class='jspsych-btn'>Click to begin.</button>

`
        document.querySelector('#begin-validate-btn').addEventListener('click', function(){
          validate();
        });
      }

      var points_completed = -1;
      var val_points = null;

      function validate(){

        if(trial.randomize_validation_order){
          val_points = jsPsych.randomization.shuffle(trial.validation_points);
        } else {
          val_points = trial.validation_points;
        }
        points_completed = -1;
        jsPsych.extensions['webgazer'].resume();
        //jsPsych.extensions.webgazer.showPredictions();
        next_validation_point();
      }

      function next_validation_point(){
        points_completed++;
        if(points_completed == val_points.length){
          validation_done();
        } else {
          var pt = val_points[points_completed];
          validation_display(pt);
        }
      }

      function validation_display(pt){
        var pt_html = drawValidationPoint(pt[0], pt[1]);
        wg_container.innerHTML = pt_html;

        var pt_dom = wg_container.querySelector('.validation-point');

        var br = pt_dom.getBoundingClientRect();
        var x = br.left + br.width / 2;
        var y = br.top + br.height / 2;

        var pt_start_val = performance.now() + trial.time_to_saccade;
        var pt_finish = pt_start_val + trial.validation_duration;

        var pt_data = [];

        requestAnimationFrame(function watch_dot(){

          if(performance.now() > pt_start_val){
            jsPsych.extensions['webgazer'].getCurrentPrediction().then(function(prediction){
              pt_data.push({dx: prediction.x - x, dy: prediction.y - y});
            });
          }
          if(performance.now() < pt_finish){
            requestAnimationFrame(watch_dot);
          } else {
            trial_data.raw_gaze.push(pt_data);
            next_validation_point();
          }
        });

      }

      function drawValidationPoint(x,y){
        if(trial.validation_point_coordinates == 'percent'){
          return drawValidationPoint_PercentMode(x,y);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawValidationPoint_CenterOffsetMode(x,y);
        }
      }

      function drawValidationPoint_PercentMode(x,y){
        return `

`
      }

      function drawValidationPoint_CenterOffsetMode(x,y){
        return `

`
      }

      function drawCircle(target_x, target_y, dx, dy, r){
        if(trial.validation_point_coordinates == 'percent'){
          return drawCircle_PercentMode(target_x, target_y, dx, dy, r);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r);
        }
      }

      function drawCircle_PercentMode(target_x, target_y, dx, dy, r){
        var html = `

        `
        return html;
      }

      function drawCircle_CenterOffsetMode(target_x, target_y, dx, dy, r){
        var html = `

        `
        return html;
      }

      function drawRawDataPoint(target_x, target_y, dx, dy, ){
        if(trial.validation_point_coordinates == 'percent'){
          return drawRawDataPoint_PercentMode(target_x, target_y, dx, dy);
        }
        if(trial.validation_point_coordinates == 'center-offset-pixels'){
          return drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy);
        }
      }

      function drawRawDataPoint_PercentMode(target_x, target_y, dx, dy){
        var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
        return `

`
      }

      function drawRawDataPoint_CenterOffsetMode(target_x, target_y, dx, dy){
        var color = Math.sqrt(dx*dx + dy*dy) <= trial.roi_radius ? '#afa' : '#faa';
        return `

`
      }

      function median(arr){
        var mid = Math.floor(arr.length/2);
        var sorted_arr = arr.sort((a,b) => a-b);
        if(arr.length % 2 == 0){
          return sorted_arr[mid-1] + sorted_arr[mid] / 2;
        } else {
          return sorted_arr[mid];
        }
      }

      function calculateGazeCentroid(gazeData){

        var x_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
          accumulator += currentValue.dx;
          if(index == gazeData.length-1){
            return accumulator / gazeData.length;
          } else {
            return accumulator;
          }
        }, 0);

        var y_diff_m = gazeData.reduce(function(accumulator, currentValue, index){
          accumulator += currentValue.dy;
          if(index == gazeData.length-1){
            return accumulator / gazeData.length;
          } else {
            return accumulator;
          }
        }, 0);

        var median_distance = median(gazeData.map(function(x){ return(Math.sqrt(Math.pow(x.dx-x_diff_m,2) + Math.pow(x.dy-y_diff_m,2)))}));

        return {
          x: x_diff_m,
          y: y_diff_m,
          r: median_distance
        }
      }

      function calculatePercentInROI(gazeData){
        var distances = gazeData.map(function(p){
          return(Math.sqrt(Math.pow(p.dx,2) + Math.pow(p.dy,2)))
        });
        var sum_in_roi = distances.reduce(function(accumulator, currentValue){
          if(currentValue <= trial.roi_radius){
            accumulator++;
          }
          return accumulator;
        }, 0);
        var percent = sum_in_roi / gazeData.length * 100;
        return percent;
      }

      function validation_done(){
        for(var i=0; i<trial.validation_points.length; i++){
          trial_data.percent_in_roi[i] = calculatePercentInROI(trial_data.raw_gaze[i]);
          trial_data.average_offset[i] = calculateGazeCentroid(trial_data.raw_gaze[i]);
        }
        if(trial.show_validation_data){
          show_validation_data();
        } else {
          end_trial();
        }
      }

      function show_validation_data(){
        var html = '';
        for(var i=0; i<trial.validation_points.length; i++){
          html += drawValidationPoint(trial.validation_points[i][0], trial.validation_points[i][1]);
          html += drawCircle(trial.validation_points[i][0], trial.validation_points[i][1], 0, 0, trial.roi_radius);
          for(var j=0; j<trial_data.raw_gaze[i].length; j++){
            html += drawRawDataPoint(trial.validation_points[i][0], trial.validation_points[i][1], trial_data.raw_gaze[i][j].dx, trial_data.raw_gaze[i][j].dy)
          }
        }
        // debugging
        html += '<button id="cont" style="position:absolute; top: 50%; left:calc(50% - 50px); width: 100px;" class="jspsych-btn">Continue</btn>';
        wg_container.innerHTML = html;
        wg_container.querySelector('#cont').addEventListener('click', end_trial);
        jsPsych.extensions.webgazer.showPredictions();
      }

      // function to end trial when it is time
      function end_trial() {
        jsPsych.extensions['webgazer'].pause();
        jsPsych.extensions['webgazer'].hidePredictions();
        jsPsych.extensions['webgazer'].hideVideo();

        // kill any remaining setTimeout handlers
        jsPsych.pluginAPI.clearAllTimeouts();

        // clear the display
        display_element.innerHTML = '';

        // move on to the next trial
        jsPsych.finishTrial(trial_data);
      };

    };

    return plugin;
  })();<|endoftext|>