<commit_before>'use strict';

const log = require('npmlog');
const Promise = require('bluebird');
const got = require('got');
const hostedGitInfo = require('hosted-git-info');
const promiseRetry = require('promise-retry');
const moment = require('moment');
const deepCompact = require('deep-compact');
const tokenDealer = require('token-dealer');
const uniqBy = require('lodash/uniqBy');
const pick = require('lodash/pick');
const pull = require('lodash/pull');
const promisePropsSettled = require('./util/promisePropsSettled');
const pointsToRanges = require('./util/pointsToRanges');
const gotRetries = require('../util/gotRetries');

const logPrefix = 'collect/github';

/**
 * Extract commits based on the /stats/commit_activity response.
 *
 * @param {object} commitActivity The commit activity response
 *
 * @return {array} The commits
 */
function extractCommits(commitActivity) {
    // Aggregate the commit activity into ranges
    const points = commitActivity.map((entry) => {
        return { date: moment.unix(entry.week).utc(), count: entry.total };
    });
    const ranges = pointsToRanges(points, pointsToRanges.bucketsFromBreakpoints([7, 30, 90, 180, 365]));

    // Finally map to a prettier array based on the ranges
    return ranges.map((range) => {
        return {
            from: range.from,
            to: range.to,
            count: range.points.reduce((sum, point) => sum + point.count, 0),
        };
    });
}

/**
 * Utility function to do a request to the GitHub API.
 *
 * @param {string} resource The resource path
 * @param {object} options  The options inferred from github() options
 *
 * @return {Promise} The promise for GitHub response
 */
function githubRequest(resource, options) {
    const url = `https://api.github.com${resource}`;

    return promiseRetry((retry) => {
        // Use token dealer to circumvent rate limit issues
        return tokenDealer(options.tokens, (token, exhaust) => {
            const handleResponse = (response, err) => {
                if (response.headers['x-ratelimit-remaining'] === '0') {
                    log.warn(logPrefix, `Token ${token.substr(0, 10)}.. exhausted`);
                    exhaust(Number(response.headers['x-ratelimit-reset']) * 1000, err && err.statusCode === 403);
                }
            };

            return got(url, {
                json: true,
                timeout: 15000,
                headers: token ? { Authorization: `token ${token}` } : null,
                retries: gotRetries,
            })
            .then((response) => {
                handleResponse(response);
                return response;
            }, (err) => {
                err.response && handleResponse(err.response, err);
                throw err;
            });
        }, {
            group: 'github',
            wait: (token, duration) => {
                if (!options.waitRateLimit) {
                    return false;
                }

                duration = Math.ceil(duration / 1000 / 60);
                log.stat(logPrefix, `All tokens are exhausted, next one will become available in ${duration} minutes`);
                return true;
            },
        })
        .then((response) => {
            // If response is 202, it means that there's no cached result so we must
            // wait a bit and try again
            if (response.statusCode === 202) {
                log.verbose(logPrefix, `Got 202 response for ${url} (not cached)`);
                retry(new Error(`Got 202 response for ${url} (not cached)`));
            }

            return response.body;
        }, (err) => {
            // If status is 404, simply return null
            if (err.statusCode === 404) {
                log.warn(logPrefix, `GitHub request to ${url} failed with 404`, { err });
                return null;
            }

            log.error(logPrefix, `GitHub request to ${url} failed`, { err });
            throw err;
        });
    }, { minTimeout: 2500, retries: 5 });
}

/**
 * Utility function to do a request to the Issue Stats API.
 *
 * @param {string} resource The resource path
 *
 * @return {Promise} The promise for GitHub response
 */
function issueStatsRequest(resource) {
    const url = `http://issuestats.com${resource}?format=json`;

    return promiseRetry((retry) => {
        return got(url, { timeout: 15000, retries: gotRetries })
        .then((response) => {
            let issueStats;

            // Need to manually parse the JSON because we need to test for 404 responses
            // See: https://github.com/hstove/issue_stats/issues/38
            try {
                issueStats = JSON.parse(response.body);
            } catch (err) {
                if (/couldn't find that page/i.test(response.body)) {
                    log.verbose(logPrefix, `Issue Stats request to ${url} failed because the page does not exist`);
                    return null;
                }

                // If the page still seems invalid, try again.. it usually works next time
                err = Object.assign(new Error('Issue Stats response is not valid JSON'),
                    { url, response: `${response.body.substr(0, 2500)}..` });

                log.warn(logPrefix, `Issue Stats response to ${url} is not valid JSON`, { err });
                retry(err);
            }

            // Check if the results are valid or if we need to wait a bit and try again
            if (issueStats.issues_count == null) {
                log.verbose(logPrefix, `Issue Stats response for ${url} does not have valid information yet`);
                retry(new Error(`Issue Stats response for ${url} does not have valid information yet`));
            }

            return issueStats;
        }, (err) => {
            // If status is 404, simply return null
            if (err.statusCode === 404) {
                log.warn(logPrefix, `Issue Stats request to ${url} failed with 404`, { err });
                return null;
            }

            log.error(logPrefix, `Issue Stats request to ${url} failed`, { err });
            throw err;
        });
    }, { minTimeout: 2500, retries: 5 });
}

// ----------------------------------------------------------------------------

/**
 * Runs the github analyzer.
 * If the repository is not hosted in GitHub, the promise resolves to `null`.
 *
 * @param {object} data        The module data
 * @param {object} packageJson The latest package.json data (normalized)
 * @param {object} [options]   The options; read bellow to get to know each available option
 *
 * @return {Promise} The promise that fulfills when done
 */
function github(data, packageJson, options) {
    const repository = packageJson.repository;

    if (!repository) {
        log.verbose(logPrefix, `No repository field present for ${data.name}, ignoring..`);
        return Promise.resolve(null);
    }

    const gitInfo = hostedGitInfo.fromUrl(repository.url);

    if (!gitInfo || gitInfo.type !== 'github') {
        log.verbose(logPrefix, `Repository for ${data.name} is not hosted on GitHub, ignoring..`, { repository });
        return Promise.resolve(null);
    }

    options = Object.assign({
        tokens: null,          // The GitHub API token to use
        waitRateLimit: false,  // True to wait if handle rate limit for all tokens were exceeded
    }, options);

    const shorthand = `${gitInfo.user}/${gitInfo.project}`;
    const ref = packageJson.gitHead || 'master';

    return promisePropsSettled({
        info: githubRequest(`/repos/${shorthand}`, options),
        contributors: githubRequest(`/repos/${shorthand}/stats/contributors`, options),
        commitActivity: githubRequest(`/repos/${shorthand}/stats/commit_activity`, options),
        statuses: githubRequest(`/repos/${shorthand}/commits/${ref}/statuses`, options),
        issueStats: issueStatsRequest(`/github/${shorthand}`, options),
    })
    .then((data) => {
        const missingData = pull(Object.keys(data).filter((key) => !data[key]), 'issueStats');

        if (missingData.length) {
            log.warn(logPrefix, 'Can\'t analyze because there\'s missing data', { missingData });
            return null;
        }

        return deepCompact({
            homepage: data.info.homepage,

            starsCount: data.info.stargazers_count,
            forksCount: data.info.forks_count,
            subscribersCount: data.info.subscribers_count,

            // <3 http://issuestats.com/
            issues: {
                isDisabled: !data.info.has_issues,
                count: data.issueStats && data.issueStats.issues_count,
                openCount: data.issueStats && data.issueStats.open_issues_count,
                distribution: data.issueStats && data.issueStats.basic_distribution,
            },

            // Contributors (top 100)
            contributors: data.contributors
            .map((contributor) => {
                const author = contributor.author;

                // Empty entries will be stripped by deepCompact
                return author && { username: contributor.author.login, commitsCount: contributor.total };
            })
            .reverse(),

            // Commit activity
            commits: extractCommits(data.commitActivity),

            // Statuses
            statuses: uniqBy(data.statuses, (status) => status.context)
            .map((status) => pick(status, 'context', 'state')),
        });
    });
}

module.exports = github;
<commit_msg>Minor tweak to logs.<commit_after>'use strict';

const log = require('npmlog');
const Promise = require('bluebird');
const got = require('got');
const hostedGitInfo = require('hosted-git-info');
const promiseRetry = require('promise-retry');
const moment = require('moment');
const deepCompact = require('deep-compact');
const tokenDealer = require('token-dealer');
const uniqBy = require('lodash/uniqBy');
const pick = require('lodash/pick');
const pull = require('lodash/pull');
const promisePropsSettled = require('./util/promisePropsSettled');
const pointsToRanges = require('./util/pointsToRanges');
const gotRetries = require('../util/gotRetries');

const logPrefix = 'collect/github';

/**
 * Extract commits based on the /stats/commit_activity response.
 *
 * @param {object} commitActivity The commit activity response
 *
 * @return {array} The commits
 */
function extractCommits(commitActivity) {
    // Aggregate the commit activity into ranges
    const points = commitActivity.map((entry) => {
        return { date: moment.unix(entry.week).utc(), count: entry.total };
    });
    const ranges = pointsToRanges(points, pointsToRanges.bucketsFromBreakpoints([7, 30, 90, 180, 365]));

    // Finally map to a prettier array based on the ranges
    return ranges.map((range) => {
        return {
            from: range.from,
            to: range.to,
            count: range.points.reduce((sum, point) => sum + point.count, 0),
        };
    });
}

/**
 * Utility function to do a request to the GitHub API.
 *
 * @param {string} resource The resource path
 * @param {object} options  The options inferred from github() options
 *
 * @return {Promise} The promise for GitHub response
 */
function githubRequest(resource, options) {
    const url = `https://api.github.com${resource}`;

    return promiseRetry((retry) => {
        // Use token dealer to circumvent rate limit issues
        return tokenDealer(options.tokens, (token, exhaust) => {
            const handleResponse = (response, err) => {
                if (response.headers['x-ratelimit-remaining'] === '0') {
                    log.warn(logPrefix, `Token ${token.substr(0, 10)}.. exhausted`);
                    exhaust(Number(response.headers['x-ratelimit-reset']) * 1000, err && err.statusCode === 403);
                }
            };

            return got(url, {
                json: true,
                timeout: 15000,
                headers: token ? { Authorization: `token ${token}` } : null,
                retries: gotRetries,
            })
            .then((response) => {
                handleResponse(response);
                return response;
            }, (err) => {
                err.response && handleResponse(err.response, err);
                throw err;
            });
        }, {
            group: 'github',
            wait: (token, duration) => {
                if (!options.waitRateLimit) {
                    return false;
                }

                duration = Math.ceil(duration / 1000 / 60);
                log.stat(logPrefix, `All tokens are exhausted, next one will become available in ${duration} minutes`);
                return true;
            },
        })
        .then((response) => {
            // If response is 202, it means that there's no cached result so we must
            // wait a bit and try again
            if (response.statusCode === 202) {
                log.verbose(logPrefix, `Got 202 response for ${url} (not cached)`);
                retry(new Error(`Got 202 response for ${url} (not cached)`));
            }

            return response.body;
        }, (err) => {
            // If status is 404, simply return null
            if (err.statusCode === 404) {
                log.warn(logPrefix, `GitHub request to ${url} failed with 404`, { err });
                return null;
            }

            log.error(logPrefix, `GitHub request to ${url} failed`, { err });
            throw err;
        });
    }, { minTimeout: 2500, retries: 5 });
}

/**
 * Utility function to do a request to the Issue Stats API.
 *
 * @param {string} resource The resource path
 *
 * @return {Promise} The promise for GitHub response
 */
function issueStatsRequest(resource) {
    const url = `http://issuestats.com${resource}?format=json`;

    return promiseRetry((retry) => {
        return got(url, { timeout: 15000, retries: gotRetries })
        .then((response) => {
            let issueStats;

            // Need to manually parse the JSON because we need to test for 404 responses
            // See: https://github.com/hstove/issue_stats/issues/38
            try {
                issueStats = JSON.parse(response.body);
            } catch (err) {
                if (/couldn't find that page/i.test(response.body)) {
                    log.verbose(logPrefix, `Issue Stats request to ${url} failed because the page does not exist`);
                    return null;
                }

                // If the page is invalid, try again.. it usually works next time
                err = Object.assign(new Error('Issue Stats response is not valid JSON'),
                    { url, response: `${response.body.substr(0, 2500)}..` });

                log.warn(logPrefix, `Issue Stats response to ${url} is not valid JSON`, { err });
                retry(err);
            }

            // Check if the results are valid or if we need to wait a bit and try again
            if (issueStats.issues_count == null) {
                log.verbose(logPrefix, `Issue Stats response for ${url} does not have valid information yet`);
                retry(new Error(`Issue Stats response for ${url} does not have valid information yet`));
            }

            return issueStats;
        }, (err) => {
            // If status is 404, simply return null
            if (err.statusCode === 404) {
                log.warn(logPrefix, `Issue Stats request to ${url} failed with 404`, { err });
                return null;
            }

            log.error(logPrefix, `Issue Stats request to ${url} failed`, { err });
            throw err;
        });
    }, { minTimeout: 2500, retries: 5 });
}

// ----------------------------------------------------------------------------

/**
 * Runs the github analyzer.
 * If the repository is not hosted in GitHub, the promise resolves to `null`.
 *
 * @param {object} data        The module data
 * @param {object} packageJson The latest package.json data (normalized)
 * @param {object} [options]   The options; read bellow to get to know each available option
 *
 * @return {Promise} The promise that fulfills when done
 */
function github(data, packageJson, options) {
    const repository = packageJson.repository;

    if (!repository) {
        log.verbose(logPrefix, `No repository field present for ${data.name}, ignoring..`);
        return Promise.resolve(null);
    }

    const gitInfo = hostedGitInfo.fromUrl(repository.url);

    if (!gitInfo || gitInfo.type !== 'github') {
        log.verbose(logPrefix, `Repository for ${data.name} is not hosted on GitHub, ignoring..`, { repository });
        return Promise.resolve(null);
    }

    options = Object.assign({
        tokens: null,          // The GitHub API token to use
        waitRateLimit: false,  // True to wait if handle rate limit for all tokens were exceeded
    }, options);

    const shorthand = `${gitInfo.user}/${gitInfo.project}`;
    const ref = packageJson.gitHead || 'master';

    return promisePropsSettled({
        info: githubRequest(`/repos/${shorthand}`, options),
        contributors: githubRequest(`/repos/${shorthand}/stats/contributors`, options),
        commitActivity: githubRequest(`/repos/${shorthand}/stats/commit_activity`, options),
        statuses: githubRequest(`/repos/${shorthand}/commits/${ref}/statuses`, options),
        issueStats: issueStatsRequest(`/github/${shorthand}`, options),
    })
    .then((data) => {
        const missingData = pull(Object.keys(data).filter((key) => !data[key]), 'issueStats');

        if (missingData.length) {
            log.warn(logPrefix, 'There\'s missing data, returning null..', { missingData });
            return null;
        }

        return deepCompact({
            homepage: data.info.homepage,

            starsCount: data.info.stargazers_count,
            forksCount: data.info.forks_count,
            subscribersCount: data.info.subscribers_count,

            // <3 http://issuestats.com/
            issues: {
                isDisabled: !data.info.has_issues,
                count: data.issueStats && data.issueStats.issues_count,
                openCount: data.issueStats && data.issueStats.open_issues_count,
                distribution: data.issueStats && data.issueStats.basic_distribution,
            },

            // Contributors (top 100)
            contributors: data.contributors
            .map((contributor) => {
                const author = contributor.author;

                // Empty entries will be stripped by deepCompact
                return author && { username: contributor.author.login, commitsCount: contributor.total };
            })
            .reverse(),

            // Commit activity
            commits: extractCommits(data.commitActivity),

            // Statuses
            statuses: uniqBy(data.statuses, (status) => status.context)
            .map((status) => pick(status, 'context', 'state')),
        });
    });
}

module.exports = github;
<|endoftext|>


<commit_before>"use strict";

var $ = require('jquery'),
    Marionette = require('../shim/backbone.marionette'),
    shutterbug = require('../shim/shutterbug'),
    views = require('./core/views'),
    LayerPickerView = require('./core/layerPicker'),
    models = require('./core/models'),
    settings = require('./core/settings'),
    itsi = require('./core/itsiEmbed'),
    analyzeModels = require('./analyze/models'),
    userModels = require('./user/models'),
    userViews = require('./user/views');

var App = new Marionette.Application({
    initialize: function() {
        this.restApi = new RestAPI();
        this.map = new models.MapModel();
        this.layerTabs = new models.LayerTabCollection(null);
        this.state = new models.AppStateModel();

        // If in embed mode we are by default in activity mode.
        var activityMode = settings.get('itsi_enabled') &&
                settings.get('itsi_embed');
        settings.set('activityMode', activityMode);

        // Initialize embed interface if in activity mode
        if (activityMode) {
            this.itsi = new itsi.ItsiEmbed(this);
            this.itsi.getAuthInfo();
        }

        // This view is intentionally not attached to any region.
        this._mapView = new views.MapView({
            model: this.map,
            layerTabCollection: this.layerTabs,
            el: '#map'
        });

        this._mapView.on('change:needs_reset', function(needs) {
            App.currentProject.set('needs_reset', needs);
        });

        this.layerTabs.disableLayersOnZoomAndPan(this.getLeafletMap());

        this.rootView = new views.RootView({app: this});
        this.user = new userModels.UserModel({});

        this.header = new views.HeaderView({
            el: 'header',
            model: this.user,
            appState: this.state
        });

        this.header.render();

        if (settings.isLayerSelectorEnabled()) {
            this.showLayerPicker();
        }

        // Not set until modeling/controllers.js creates a
        // new project.
        this.currentProject = null;

        // Enable screenshot functionality
        initializeShutterbug();
    },

    load: function(data) {
        var mapState = data.map;
        if (mapState) {
            this.map.set({
                lat: mapState.lat,
                lng: mapState.lng,
                zoom: mapState.zoom
            });
        }
    },

    getLayerTabCollection: function() {
        return this.layerTabs;
    },

    showLayerPicker: function() {
        this.layerPickerView = new LayerPickerView({
            collection: this.layerTabs,
            leafletMap: this.getLeafletMap(),
        });
        this.rootView.layerPickerRegion.show(this.layerPickerView);
    },

    getAnalyzeCollection: function() {
        if (!this.analyzeCollection) {
            var aoi = this.map.get('areaOfInterest'),
                wkaoi = this.map.get('wellKnownAreaOfInterest');

            this.analyzeCollection = analyzeModels.createAnalyzeTaskCollection(aoi, wkaoi);
        }

        return this.analyzeCollection;
    },

    clearAnalyzeCollection: function() {
        delete this.analyzeCollection;
    },

    getMapView: function() {
        return this._mapView;
    },

    getLeafletMap: function() {
        return this._mapView._leafletMap;
    },

    getUserOrShowLoginIfNotItsiEmbed: function() {
        if (!settings.get('itsi_embed')) {
            this.getUserOrShowLogin();
        }
    },

    getUserOrShowLogin: function(onSuccess) {
        this.user.fetch().always(function() {
            if (App.user.get('guest')) {
                App.showLoginModal(onSuccess);
            }
        });
    },

    showLoginModal: function(onSuccess) {
        new userViews.LoginModalView({
            model: new userModels.LoginFormModel({
                showItsiButton: settings.get('itsi_enabled'),
                successCallback: onSuccess
            }),
            app: this
        }).render();
    }
});

function RestAPI() {
    return {
        getPolygon: function(args) {
            var url = '/api/modeling/boundary-layers/' + args.layerCode + '/' + args.shapeId;
            return $.ajax({
                'url': url,
                'type': 'GET'
            });
        }
    };
}

function initializeShutterbug() {
    var googleTileLayerSelector = '#map > .leaflet-google-layer > div > div > div:nth-child(1) > div:nth-child(1)';

    $(window)
        .on('shutterbug-saycheese', function() {
            // Set fixed width before screenshot to constrain width to viewport
            $('#model-output-wrapper, body > .map-container').css({
                'width': window.innerWidth
            });

            var activeBaseLayer = App.getLayerTabCollection().getCurrentActiveBaseLayer(),
                googleLayerVisible = !!activeBaseLayer.get('leafletLayer')._google;

            if (googleLayerVisible) {
                // Convert Google Maps CSS Transforms to Left / Right
                var $googleTileLayer = $(googleTileLayerSelector),
                    transform = $googleTileLayer.css('transform').split(','),
                    left = parseFloat(transform[4]),
                    top = parseFloat(transform[5]);

                $googleTileLayer.css({
                    transform: 'none',
                    left: left,
                    top: top,
                });
            }

            // Fix Firefox screenshots by adding a '#' to the URL.
            // Setting to empty string does nothing, so we first set to
            // '/' then to empty string, which leaves a '#' in the URL.
            document.location.hash = '/';
            document.location.hash = '';
        })
        .on('shutterbug-asyouwere', function() {
            // Reset after screenshot has been taken
            $('#model-output-wrapper, body > .map-container').css({
                'width': ''
            });

            var activeBaseLayer = App.getLayerTabCollection().getCurrentActiveBaseLayer(),
                googleLayerVisible = !!activeBaseLayer.get('leafletLayer')._google;

            if (googleLayerVisible) {
                var $googleTileLayer = $(googleTileLayerSelector),
                    left = parseFloat($googleTileLayer.css('left')),
                    top = parseFloat($googleTileLayer.css('top')),
                    transform = 'matrix(1, 0, 0, 1, ' + left + ', ' + top + ')';

                $googleTileLayer.css({
                    transform: transform,
                    left: '',
                    top: '',
                });
            }
        });

    shutterbug.enable('body');
}

module.exports = App;
<commit_msg>Revert "Allow Passing Of Success-Callback To Login/Signup Prompt"<commit_after>"use strict";

var $ = require('jquery'),
    Marionette = require('../shim/backbone.marionette'),
    shutterbug = require('../shim/shutterbug'),
    views = require('./core/views'),
    LayerPickerView = require('./core/layerPicker'),
    models = require('./core/models'),
    settings = require('./core/settings'),
    itsi = require('./core/itsiEmbed'),
    analyzeModels = require('./analyze/models'),
    userModels = require('./user/models'),
    userViews = require('./user/views');

var App = new Marionette.Application({
    initialize: function() {
        this.restApi = new RestAPI();
        this.map = new models.MapModel();
        this.layerTabs = new models.LayerTabCollection(null);
        this.state = new models.AppStateModel();

        // If in embed mode we are by default in activity mode.
        var activityMode = settings.get('itsi_enabled') &&
                settings.get('itsi_embed');
        settings.set('activityMode', activityMode);

        // Initialize embed interface if in activity mode
        if (activityMode) {
            this.itsi = new itsi.ItsiEmbed(this);
            this.itsi.getAuthInfo();
        }

        // This view is intentionally not attached to any region.
        this._mapView = new views.MapView({
            model: this.map,
            layerTabCollection: this.layerTabs,
            el: '#map'
        });

        this._mapView.on('change:needs_reset', function(needs) {
            App.currentProject.set('needs_reset', needs);
        });

        this.layerTabs.disableLayersOnZoomAndPan(this.getLeafletMap());

        this.rootView = new views.RootView({app: this});
        this.user = new userModels.UserModel({});

        this.header = new views.HeaderView({
            el: 'header',
            model: this.user,
            appState: this.state
        });

        this.header.render();

        if (settings.isLayerSelectorEnabled()) {
            this.showLayerPicker();
        }

        // Not set until modeling/controllers.js creates a
        // new project.
        this.currentProject = null;

        // Enable screenshot functionality
        initializeShutterbug();
    },

    load: function(data) {
        var mapState = data.map;
        if (mapState) {
            this.map.set({
                lat: mapState.lat,
                lng: mapState.lng,
                zoom: mapState.zoom
            });
        }
    },

    getLayerTabCollection: function() {
        return this.layerTabs;
    },

    showLayerPicker: function() {
        this.layerPickerView = new LayerPickerView({
            collection: this.layerTabs,
            leafletMap: this.getLeafletMap(),
        });
        this.rootView.layerPickerRegion.show(this.layerPickerView);
    },

    getAnalyzeCollection: function() {
        if (!this.analyzeCollection) {
            var aoi = this.map.get('areaOfInterest'),
                wkaoi = this.map.get('wellKnownAreaOfInterest');

            this.analyzeCollection = analyzeModels.createAnalyzeTaskCollection(aoi, wkaoi);
        }

        return this.analyzeCollection;
    },

    clearAnalyzeCollection: function() {
        delete this.analyzeCollection;
    },

    getMapView: function() {
        return this._mapView;
    },

    getLeafletMap: function() {
        return this._mapView._leafletMap;
    },

    getUserOrShowLoginIfNotItsiEmbed: function() {
        if (!settings.get('itsi_embed')) {
            this.getUserOrShowLogin();
        }
    },

    getUserOrShowLogin: function() {
        this.user.fetch().always(function() {
            if (App.user.get('guest')) {
                App.showLoginModal();
            }
        });
    },

    showLoginModal: function(onSuccess) {
        new userViews.LoginModalView({
            model: new userModels.LoginFormModel({
                showItsiButton: settings.get('itsi_enabled'),
                successCallback: onSuccess
            }),
            app: this
        }).render();
    }
});

function RestAPI() {
    return {
        getPolygon: function(args) {
            var url = '/api/modeling/boundary-layers/' + args.layerCode + '/' + args.shapeId;
            return $.ajax({
                'url': url,
                'type': 'GET'
            });
        }
    };
}

function initializeShutterbug() {
    var googleTileLayerSelector = '#map > .leaflet-google-layer > div > div > div:nth-child(1) > div:nth-child(1)';

    $(window)
        .on('shutterbug-saycheese', function() {
            // Set fixed width before screenshot to constrain width to viewport
            $('#model-output-wrapper, body > .map-container').css({
                'width': window.innerWidth
            });

            var activeBaseLayer = App.getLayerTabCollection().getCurrentActiveBaseLayer(),
                googleLayerVisible = !!activeBaseLayer.get('leafletLayer')._google;

            if (googleLayerVisible) {
                // Convert Google Maps CSS Transforms to Left / Right
                var $googleTileLayer = $(googleTileLayerSelector),
                    transform = $googleTileLayer.css('transform').split(','),
                    left = parseFloat(transform[4]),
                    top = parseFloat(transform[5]);

                $googleTileLayer.css({
                    transform: 'none',
                    left: left,
                    top: top,
                });
            }

            // Fix Firefox screenshots by adding a '#' to the URL.
            // Setting to empty string does nothing, so we first set to
            // '/' then to empty string, which leaves a '#' in the URL.
            document.location.hash = '/';
            document.location.hash = '';
        })
        .on('shutterbug-asyouwere', function() {
            // Reset after screenshot has been taken
            $('#model-output-wrapper, body > .map-container').css({
                'width': ''
            });

            var activeBaseLayer = App.getLayerTabCollection().getCurrentActiveBaseLayer(),
                googleLayerVisible = !!activeBaseLayer.get('leafletLayer')._google;

            if (googleLayerVisible) {
                var $googleTileLayer = $(googleTileLayerSelector),
                    left = parseFloat($googleTileLayer.css('left')),
                    top = parseFloat($googleTileLayer.css('top')),
                    transform = 'matrix(1, 0, 0, 1, ' + left + ', ' + top + ')';

                $googleTileLayer.css({
                    transform: transform,
                    left: '',
                    top: '',
                });
            }
        });

    shutterbug.enable('body');
}

module.exports = App;
<|endoftext|>


<commit_before>// TODO: add functionality that allows you to stop listening to messages
GT.WaterCooler = (function() {
    // initialise variables
    var messageListeners = {},
        pipes = [];

    // define the module
    var module = {
        addPipe: function(callback) {
            // test the pipe because if it is broke it will break everything
            callback("pipe.test", {});

            // given that didn't throw an exception and we got here, we can now add the pipe
            pipes.push(callback);
        },

        listen: function(message, callback) {
            // if we don't have a message listener array configured, then create one now
            if (! messageListeners[message]) {
                messageListeners[message] = [];
            } // if

            // add the callback to the listener queue
            if (callback) {
                messageListeners[message].push(callback);
            } // if
        },

        say: function(message, args) {
            var ii;

            // if there are pipes, then send the message through each
            for (ii = pipes.length; ii--; ) {
                pipes[ii](message, args);
            } // for

            // if we don't have any message listeners for that message, then return
            if (! messageListeners[message]) { return; }

            // iterate through the message callbacks
            for (ii = messageListeners[message].length; ii--; ) {
                messageListeners[message][ii](args);
            } // for
        },

        leave: function() {

        }
    };

    return module;
})();

<commit_msg>Removed WaterCooler for terseness - now just GT.say and GT.listen<commit_after>// TODO: add functionality that allows you to stop listening to messages
(function() {
    // initialise variables
    var messageListeners = {},
        pipes = [];

    // define the module
    GT.addPipe = function(callback) {
        // test the pipe because if it is broke it will break everything
        callback("pipe.test", {});

        // given that didn't throw an exception and we got here, we can now add the pipe
        pipes.push(callback);
    }; // addPipe

    GT.listen = function(message, callback) {
        // if we don't have a message listener array configured, then create one now
        if (! messageListeners[message]) {
            messageListeners[message] = [];
        } // if

        // add the callback to the listener queue
        if (callback) {
            messageListeners[message].push(callback);
        } // if
    }; // listen

    GT.say = function(message, args) {
        var ii;

        // if there are pipes, then send the message through each
        for (ii = pipes.length; ii--; ) {
            pipes[ii](message, args);
        } // for

        // if we don't have any message listeners for that message, then return
        if (! messageListeners[message]) { return; }

        // iterate through the message callbacks
        for (ii = messageListeners[message].length; ii--; ) {
            messageListeners[message][ii](args);
        } // for
    }; // say
})();

<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

var gulp = require('gulp');
var replace = require('gulp-replace-task');
var fs = require('fs');
var g = require('gulp-load-plugins')({lazy: false});
var noop = g.util.noop;
var es = require('event-stream');
var Queue = require('streamqueue');
var lazypipe = require('lazypipe');
var stylish = require('jshint-stylish');
var bower = require('./bower');
var mainBowerFiles = require('main-bower-files');
var historyApiFallback = require('connect-history-api-fallback');
var isWatching = false;

var htmlminOpts = {
  removeComments: true,
  collapseWhitespace: true,
  removeEmptyAttributes: false,
  collapseBooleanAttributes: true,
  removeRedundantAttributes: true
};

var settings;

// Try to read frontend configuration file, fallback to default file
try {
  settings = JSON.parse(fs.readFileSync('./config/config.json', 'utf8'));
} catch (error) {
  settings = JSON.parse(fs.readFileSync('./config/config_example.json', 'utf8'));
}

var KONGA_BACKEND_URL = process.env.KONGA_BACKEND_URL || settings.backendUrl

// Overwrite port if explicitly set in command
var argv = require('minimist')(process.argv.slice(2));
if(argv.p) {
    settings.frontend.ports.production = argv.p
    settings.frontend.ports.development = argv.p
}

/**
 * JS Hint
 */
gulp.task('jshint', function() {
  return gulp.src([
        './gulpfile.js',
        './src/app/**/*.js'
      ])
      .pipe(g.cached('jshint'))
      .pipe(jshint('./.jshintrc'))
      .pipe(livereload());
});

/**
 * CSS
 */
gulp.task('clean-css', function() {
  return gulp.src('./.tmp/css').pipe(g.clean());
});

gulp.task('styles', ['clean-css'], function() {
  return gulp.src([
        './src/app/**/*.scss',
        '!./src/app/**/_*.scss'
      ])
      .pipe(g.sass())
      .pipe(gulp.dest('./.tmp/css/'))
      .pipe(g.cached('built-css'))
      .pipe(livereload());
});

gulp.task('styles-dist', ['styles'], function() {
  return cssFiles().pipe(dist('css', bower.name));
});

gulp.task('csslint', ['styles'], function() {
  return cssFiles()
      .pipe(g.cached('csslint'))
      .pipe(g.csslint('./.csslintrc'))
      .pipe(g.csslint.reporter())
      ;
});

/**
 * Scripts
 */
gulp.task('scripts-dist', ['templates-dist'], function() {
  return appFiles().pipe(dist('js', bower.name, {ngAnnotate: true}));
});

/**
 * Templates
 */
gulp.task('templates', function() {
  return templateFiles().pipe(buildTemplates());
});

gulp.task('templates-dist', function() {
  return templateFiles({min: true}).pipe(buildTemplates());
});

/**
 * Vendors
 */
gulp.task('vendors', function() {
  var bowerStream = gulp.src(mainBowerFiles());

  return es.merge(
      bowerStream.pipe(g.filter('**/*.css')).pipe(dist('css', 'vendors')),
      bowerStream.pipe(g.filter('**/*.js')).pipe(dist('js', 'vendors'))
  );
});

/**
 * Index
 */
gulp.task('index', index);
gulp.task('build-all', ['styles', 'templates'], index);

function index() {
  var opt = {read: false};

  return gulp.src('./src/app/index.html')
      .pipe(g.inject(gulp.src(mainBowerFiles(opt)), {ignorePath: 'bower_components', starttag: '<!-- inject:vendor:{{ext}} -->'}))
      .pipe(g.inject(es.merge(appFiles(), cssFiles(opt)), {ignorePath: ['.tmp', 'src/app']}))
      .pipe(g.embedlr())
      .pipe(replace({
        patterns: [
            {
                match: 'backendUrl',
                replacement: KONGA_BACKEND_URL || settings.backendUrl
            },
            {
                match: 'enableLogs',
                replacement: process.env.KONGA_FRONTEND_LOGS || true
            }
        ]
      }))
      .pipe(gulp.dest('./.tmp/'))
      .pipe(livereload())
      ;
}

/**
 * Assets
 */
gulp.task('assets', function() {
  return gulp.src('./src/app/assets/**')
      .pipe(gulp.dest('./dist/assets'))
      ;
});

/**
 * Partials
 */
gulp.task('partials', function() {
  return gulp.src('./src/app/partials/**')
      .pipe(gulp.dest('./dist/partials'))
      ;
});

/**
 * Fonts
 */
//gulp.task('fonts', function() {
//  return gulp.src('./bower_components/fontawesome/fonts/**')
//      .pipe(gulp.dest('./dist/fonts'))
//      ;
//});

/**
 * Dist
 */
gulp.task('dist', ['vendors', 'assets', 'styles-dist', 'scripts-dist'], function() {
  return gulp.src('./src/app/index.html')
      .pipe(g.inject(gulp.src('./dist/vendors.min.{js,css}'), {
        ignorePath: 'dist',
        starttag: '<!-- inject:vendor:{{ext}} -->',
          addRootSlash:false
      }))
      .pipe(g.inject(gulp.src('./dist/' + bower.name + '.min.{js,css}'), {ignorePath: 'dist',addRootSlash:false}))
      .pipe(replace({
        patterns: [
            {
                match: 'backendUrl',
                replacement: KONGA_BACKEND_URL || settings.backendUrl
            },
            {
                match: 'enableLogs',
                replacement: process.env.KONGA_FRONTEND_LOGS || false
            }
        ]
      }))
      .pipe(g.htmlmin(htmlminOpts))
      .pipe(gulp.dest('./dist/'))
      ;
});

/**
 * Static file server
 */
gulp.task('statics', g.serve({
  port: settings.frontend.ports.development,
  root: ['./.tmp', './src/app', './bower_components'],
  middleware: historyApiFallback({})
}));

/**
 * Production file server, note remember to run 'gulp dist' first!
 */
gulp.task('production',g.serve({
    port: settings.frontend.ports.production,
    root: ['./dist'],
    middleware: historyApiFallback({})
}));

/**
 * Watch
 */
gulp.task('serve', ['watch']);

gulp.task('watch', ['statics', 'default'], function() {
  isWatching = true;

  // Initiate livereload server:
  g.livereload({
    start: true
  });

  gulp.watch('./src/app/**/*.js', ['jshint']).on('change', function(evt) {
    if (evt.type !== 'changed') {
      gulp.start('index');
    }
  });

  gulp.watch('./src/app/index.html', ['index']);
  gulp.watch(['./src/app/**/*.html', '!./src/app/index.html'], ['templates']);
  gulp.watch(['./src/app/**/*.scss'], ['csslint']).on('change', function(evt) {
    if (evt.type !== 'changed') {
      gulp.start('index');
    }
  });
});

/**
 * Default task
 */
gulp.task('default', ['lint', 'build-all']);

/**
 * Lint everything
 */
gulp.task('lint', ['jshint', 'csslint']);

/**
 * Test
 */
gulp.task('test', ['templates'], function() {
  return testFiles()
      .pipe(g.karma({
        configFile: __dirname + '/karma.conf.js',
        singleRun: true
      }))
      ;
});

/**
 * Inject all files for tests into karma.conf.js
 * to be able to run `karma` without gulp.
 */
gulp.task('karma-conf', ['templates'], function() {
  return gulp.src('./karma.conf.js')
      .pipe(g.inject(testFiles(), {
        starttag: 'files: [',
        endtag: ']',
        addRootSlash: false,
        transform: function(filepath, file, i, length) {
          return '  \'' + filepath + '\'' + (i + 1 < length ? ',' : '');
        }
      }))
      .pipe(gulp.dest('./'))
      ;
});

/**
 * Test files
 */
function testFiles() {
  return new Queue({objectMode: true})
      .Queue(gulp.src(mainBowerFiles()).pipe(g.filter('**/*.js')))
      .Queue(gulp.src('./bower_components/angular-mocks/angular-mocks.js'))
      .Queue(appFiles())
      .Queue(gulp.src('./src/app/**/*_test.js'))
      .done()
      ;
}

/**
 * All CSS files as a stream
 */
function cssFiles(opt) {
  return gulp.src('./.tmp/css/**/*.css', opt);
}

/**
 * All AngularJS application files as a stream
 */
function appFiles() {
  var files = [
    './.tmp/' + bower.name + '-templates.js',
    './src/app/**/*.js',
    '!./src/app/**/*_test.js'
  ];

  return gulp.src(files)
      .pipe(g.angularFilesort())
      ;
}

/**
 * All AngularJS templates/partials as a stream
 */
function templateFiles(opt) {
  return gulp.src([
        './src/app/**/*.html',
        './src/app/**/**/*.html',
        './src/app/**/**/**/*.html',
        '!./src/app/index.html'], opt)
      .pipe(opt && opt.min ? g.htmlmin(htmlminOpts) : noop())
      ;
}

/**
 * Build AngularJS templates/partials
 */
function buildTemplates() {
  return lazypipe()
      .pipe(g.ngHtml2js, {
        moduleName: bower.name + '-templates',
        prefix: '/' + bower.name + '/',
        stripPrefix: '/src/app'
      })
      .pipe(g.concat, bower.name + '-templates.js')
      .pipe(gulp.dest, './.tmp')
      .pipe(livereload)()
      ;
}

/**
 * Concat, rename, minify
 *
 * @param {String} ext
 * @param {String} name
 * @param {Object} opt
 */
function dist(ext, name, opt) {
  opt = opt || {};

  return lazypipe()
      .pipe(g.concat, name + '.' + ext)
      .pipe(gulp.dest, './dist')
      .pipe(opt.ngAnnotate ? g.ngAnnotate : noop)
      .pipe(opt.ngAnnotate ? g.rename : noop, name + '.annotated.' + ext)
      .pipe(opt.ngAnnotate ? gulp.dest : noop, './dist')
      .pipe(ext === 'js' ? g.uglify : g.minifyCss,ext === 'js' ? {} : {processImport: false})
      .pipe(g.rename, name + '.min.' + ext)
      .pipe(gulp.dest, './dist')()
      ;
}

/**
 * Livereload (or noop if not run by watch)
 */
function livereload() {
  return lazypipe()
      .pipe(isWatching ? g.livereload : noop)()
      ;
}

/**
 * Jshint with stylish reporter
 */
function jshint(jshintfile) {
  // Read JSHint settings, for some reason jshint-stylish won't work on initial load of files
  var jshintSettings = JSON.parse(fs.readFileSync(jshintfile, 'utf8'));

  return lazypipe()
      .pipe(g.jshint, jshintSettings)
      .pipe(g.jshint.reporter, stylish)()
      ;
}<commit_msg>cleanup<commit_after>/* jshint node: true */
'use strict';

var gulp = require('gulp');
var replace = require('gulp-replace-task');
var fs = require('fs');
var g = require('gulp-load-plugins')({lazy: false});
var noop = g.util.noop;
var es = require('event-stream');
var Queue = require('streamqueue');
var lazypipe = require('lazypipe');
var stylish = require('jshint-stylish');
var bower = require('./bower');
var mainBowerFiles = require('main-bower-files');
var historyApiFallback = require('connect-history-api-fallback');
var isWatching = false;

var htmlminOpts = {
  removeComments: true,
  collapseWhitespace: true,
  removeEmptyAttributes: false,
  collapseBooleanAttributes: true,
  removeRedundantAttributes: true
};

var settings;

// Try to read frontend configuration file, fallback to default file
try {
  settings = JSON.parse(fs.readFileSync('./config/config.json', 'utf8'));
} catch (error) {
  settings = JSON.parse(fs.readFileSync('./config/config_example.json', 'utf8'));
}

var KONGA_BACKEND_URL = process.env.KONGA_BACKEND_URL || settings.backendUrl

/**
 * CSS
 */
gulp.task('clean-css', function() {
  return gulp.src('./.tmp/css').pipe(g.clean());
});

gulp.task('styles', ['clean-css'], function() {
  return gulp.src([
        './src/app/**/*.scss',
        '!./src/app/**/_*.scss'
      ])
      .pipe(g.sass())
      .pipe(gulp.dest('./.tmp/css/'))
      .pipe(g.cached('built-css'))
      .pipe(livereload());
});

gulp.task('styles-dist', ['styles'], function() {
  return cssFiles().pipe(dist('css', bower.name));
});

gulp.task('csslint', ['styles'], function() {
  return cssFiles()
      .pipe(g.cached('csslint'))
      .pipe(g.csslint('./.csslintrc'))
      .pipe(g.csslint.reporter())
      ;
});

/**
 * Scripts
 */
gulp.task('scripts-dist', ['templates-dist'], function() {
  return appFiles().pipe(dist('js', bower.name, {ngAnnotate: true}));
});

/**
 * Templates
 */
gulp.task('templates', function() {
  return templateFiles().pipe(buildTemplates());
});

gulp.task('templates-dist', function() {
  return templateFiles({min: true}).pipe(buildTemplates());
});

/**
 * Vendors
 */
gulp.task('vendors', function() {
  var bowerStream = gulp.src(mainBowerFiles());

  return es.merge(
      bowerStream.pipe(g.filter('**/*.css')).pipe(dist('css', 'vendors')),
      bowerStream.pipe(g.filter('**/*.js')).pipe(dist('js', 'vendors'))
  );
});

/**
 * Index
 */
gulp.task('index', index);
gulp.task('build-all', ['styles', 'templates'], index);

function index() {
  var opt = {read: false};

  return gulp.src('./src/app/index.html')
      .pipe(g.inject(gulp.src(mainBowerFiles(opt)), {ignorePath: 'bower_components', starttag: '<!-- inject:vendor:{{ext}} -->'}))
      .pipe(g.inject(es.merge(appFiles(), cssFiles(opt)), {ignorePath: ['.tmp', 'src/app']}))
      .pipe(g.embedlr())
      .pipe(replace({
        patterns: [
            {
                match: 'backendUrl',
                replacement: KONGA_BACKEND_URL || settings.backendUrl
            },
            {
                match: 'enableLogs',
                replacement: process.env.KONGA_FRONTEND_LOGS || true
            }
        ]
      }))
      .pipe(gulp.dest('./.tmp/'))
      .pipe(livereload())
      ;
}

/**
 * Assets
 */
gulp.task('assets', function() {
  return gulp.src('./src/app/assets/**')
      .pipe(gulp.dest('./dist/assets'))
      ;
});

/**
 * Partials
 */
gulp.task('partials', function() {
  return gulp.src('./src/app/partials/**')
      .pipe(gulp.dest('./dist/partials'))
      ;
});

/**
 * Fonts
 */
//gulp.task('fonts', function() {
//  return gulp.src('./bower_components/fontawesome/fonts/**')
//      .pipe(gulp.dest('./dist/fonts'))
//      ;
//});

/**
 * Dist
 */
gulp.task('dist', ['vendors', 'assets', 'styles-dist', 'scripts-dist'], function() {
  return gulp.src('./src/app/index.html')
      .pipe(g.inject(gulp.src('./dist/vendors.min.{js,css}'), {
        ignorePath: 'dist',
        starttag: '<!-- inject:vendor:{{ext}} -->',
          addRootSlash:false
      }))
      .pipe(g.inject(gulp.src('./dist/' + bower.name + '.min.{js,css}'), {ignorePath: 'dist',addRootSlash:false}))
      .pipe(replace({
        patterns: [
            {
                match: 'backendUrl',
                replacement: KONGA_BACKEND_URL || settings.backendUrl
            },
            {
                match: 'enableLogs',
                replacement: process.env.KONGA_FRONTEND_LOGS || false
            }
        ]
      }))
      .pipe(g.htmlmin(htmlminOpts))
      .pipe(gulp.dest('./dist/'))
      ;
});

/**
 * Static file server
 */
gulp.task('statics', g.serve({
  port: settings.frontend.ports.development,
  root: ['./.tmp', './src/app', './bower_components'],
  middleware: historyApiFallback({})
}));

/**
 * Production file server, note remember to run 'gulp dist' first!
 */
gulp.task('production',g.serve({
    port: settings.frontend.ports.production,
    root: ['./dist'],
    middleware: historyApiFallback({})
}));

/**
 * Watch
 */
gulp.task('serve', ['watch']);

gulp.task('watch', ['statics', 'default'], function() {
  isWatching = true;

  // Initiate livereload server:
  g.livereload({
    start: true
  });

  gulp.watch('./src/app/**/*.js', ['jshint']).on('change', function(evt) {
    if (evt.type !== 'changed') {
      gulp.start('index');
    }
  });

  gulp.watch('./src/app/index.html', ['index']);
  gulp.watch(['./src/app/**/*.html', '!./src/app/index.html'], ['templates']);
  gulp.watch(['./src/app/**/*.scss'], ['csslint']).on('change', function(evt) {
    if (evt.type !== 'changed') {
      gulp.start('index');
    }
  });
});

/**
 * Default task
 */
gulp.task('default', ['lint', 'build-all']);

/**
 * Lint everything
 */
gulp.task('lint', ['jshint', 'csslint']);

/**
 * Test
 */
gulp.task('test', ['templates'], function() {
  return testFiles()
      .pipe(g.karma({
        configFile: __dirname + '/karma.conf.js',
        singleRun: true
      }))
      ;
});

/**
 * Inject all files for tests into karma.conf.js
 * to be able to run `karma` without gulp.
 */
gulp.task('karma-conf', ['templates'], function() {
  return gulp.src('./karma.conf.js')
      .pipe(g.inject(testFiles(), {
        starttag: 'files: [',
        endtag: ']',
        addRootSlash: false,
        transform: function(filepath, file, i, length) {
          return '  \'' + filepath + '\'' + (i + 1 < length ? ',' : '');
        }
      }))
      .pipe(gulp.dest('./'))
      ;
});

/**
 * Test files
 */
function testFiles() {
  return new Queue({objectMode: true})
      .Queue(gulp.src(mainBowerFiles()).pipe(g.filter('**/*.js')))
      .Queue(gulp.src('./bower_components/angular-mocks/angular-mocks.js'))
      .Queue(appFiles())
      .Queue(gulp.src('./src/app/**/*_test.js'))
      .done()
      ;
}

/**
 * All CSS files as a stream
 */
function cssFiles(opt) {
  return gulp.src('./.tmp/css/**/*.css', opt);
}

/**
 * All AngularJS application files as a stream
 */
function appFiles() {
  var files = [
    './.tmp/' + bower.name + '-templates.js',
    './src/app/**/*.js',
    '!./src/app/**/*_test.js'
  ];

  return gulp.src(files)
      .pipe(g.angularFilesort())
      ;
}

/**
 * All AngularJS templates/partials as a stream
 */
function templateFiles(opt) {
  return gulp.src([
        './src/app/**/*.html',
        './src/app/**/**/*.html',
        './src/app/**/**/**/*.html',
        '!./src/app/index.html'], opt)
      .pipe(opt && opt.min ? g.htmlmin(htmlminOpts) : noop())
      ;
}

/**
 * Build AngularJS templates/partials
 */
function buildTemplates() {
  return lazypipe()
      .pipe(g.ngHtml2js, {
        moduleName: bower.name + '-templates',
        prefix: '/' + bower.name + '/',
        stripPrefix: '/src/app'
      })
      .pipe(g.concat, bower.name + '-templates.js')
      .pipe(gulp.dest, './.tmp')
      .pipe(livereload)()
      ;
}

/**
 * Concat, rename, minify
 *
 * @param {String} ext
 * @param {String} name
 * @param {Object} opt
 */
function dist(ext, name, opt) {
  opt = opt || {};

  return lazypipe()
      .pipe(g.concat, name + '.' + ext)
      .pipe(gulp.dest, './dist')
      .pipe(opt.ngAnnotate ? g.ngAnnotate : noop)
      .pipe(opt.ngAnnotate ? g.rename : noop, name + '.annotated.' + ext)
      .pipe(opt.ngAnnotate ? gulp.dest : noop, './dist')
      .pipe(ext === 'js' ? g.uglify : g.minifyCss,ext === 'js' ? {} : {processImport: false})
      .pipe(g.rename, name + '.min.' + ext)
      .pipe(gulp.dest, './dist')()
      ;
}

/**
 * Livereload (or noop if not run by watch)
 */
function livereload() {
  return lazypipe()
      .pipe(isWatching ? g.livereload : noop)()
      ;
}

/**
 * Jshint with stylish reporter
 */
function jshint(jshintfile) {
  // Read JSHint settings, for some reason jshint-stylish won't work on initial load of files
  var jshintSettings = JSON.parse(fs.readFileSync(jshintfile, 'utf8'));

  return lazypipe()
      .pipe(g.jshint, jshintSettings)
      .pipe(g.jshint.reporter, stylish)()
      ;
}<|endoftext|>


<commit_before>var Loader = require('./loader');

var WebLoader = Loader.extend({
    init: function(baseURL, neverUpdate) {
        // It's easy to use precompiled templates: just include them
        // before you configure nunjucks and this will automatically
        // pick it up and use it
        this.precompiled = window.nunjucksPrecompiled || {};

        this.baseURL = baseURL || '';
        this.neverUpdate = neverUpdate;
    },

    getSource: function(name) {
        if(this.precompiled[name]) {
            return {
                src: { type: "code",
                       obj: this.precompiled[name] },
                path: name
            };
        }
        else {
            var src = this.fetch(this.baseURL + '/' + name);
            if(!src) {
                return null;
            }

            return { src: src,
                     path: name,
                     noCache: this.neverUpdate };
        }
    },

    fetch: function(url, callback) {
        // Only in the browser please
        var ajax;
        var loading = true;
        var src;

        if(window.XMLHttpRequest) { // Mozilla, Safari, ...
            ajax = new XMLHttpRequest();
        }
        else if(window.ActiveXObject) { // IE 8 and older
            ajax = new ActiveXObject("Microsoft.XMLHTTP");
        }

        ajax.onreadystatechange = function() {
            if(ajax.readyState == 4 && ajax.status == 200 && loading) {
                loading = false;
                src = ajax.responseText;
            }
        };

        url += (url.indexOf('?') === -1 ? '?' : '&') + 's=' +
               (new Date().getTime());

        // Synchronous because this API shouldn't be used in
        // production (pre-load compiled templates instead)
        ajax.open('GET', url, false);
        ajax.send();

        return src;
    }
});

module.exports = {
    WebLoader: WebLoader
};
<commit_msg>added a status check for from-file success<commit_after>var Loader = require('./loader');

var WebLoader = Loader.extend({
    init: function(baseURL, neverUpdate) {
        // It's easy to use precompiled templates: just include them
        // before you configure nunjucks and this will automatically
        // pick it up and use it
        this.precompiled = window.nunjucksPrecompiled || {};

        this.baseURL = baseURL || '';
        this.neverUpdate = neverUpdate;
    },

    getSource: function(name) {
        if(this.precompiled[name]) {
            return {
                src: { type: "code",
                       obj: this.precompiled[name] },
                path: name
            };
        }
        else {
            var src = this.fetch(this.baseURL + '/' + name);
            if(!src) {
                return null;
            }

            return { src: src,
                     path: name,
                     noCache: this.neverUpdate };
        }
    },

    fetch: function(url, callback) {
        // Only in the browser please
        var ajax;
        var loading = true;
        var src;

        if(window.XMLHttpRequest) { // Mozilla, Safari, ...
            ajax = new XMLHttpRequest();
        }
        else if(window.ActiveXObject) { // IE 8 and older
            ajax = new ActiveXObject("Microsoft.XMLHTTP");
        }

        ajax.onreadystatechange = function() {
            if(ajax.readyState === 4 && (ajax.status === 0 || ajax.status === 200) && loading) {
                loading = false;
                src = ajax.responseText;
            }
        };

        url += (url.indexOf('?') === -1 ? '?' : '&') + 's=' +
               (new Date().getTime());

        // Synchronous because this API shouldn't be used in
        // production (pre-load compiled templates instead)
        ajax.open('GET', url, false);
        ajax.send();

        return src;
    }
});

module.exports = {
    WebLoader: WebLoader
};
<|endoftext|>


<commit_before>var gulp = require('gulp'),
  concat = require('gulp-concat'),
  sass = require('gulp-sass'),
  rename = require('gulp-rename'),
  shell = require('gulp-shell'),
  traceur = require('gulp-traceur'),
  webserver = require('gulp-webserver');

// run init tasks
gulp.task('default', ['dependencies', 'js', 'html', 'sass', 'tab', 'fonts', 'css']);

// run development task
gulp.task('dev', ['testhtml', 'watch', 'serve']);

// serve the build dir
gulp.task('serve-prod', function () {
  gulp.src('build')
    .pipe(webserver({
      open: true,
      livereload: true,
      host: 'localhost',
      proxies: [{
        source: '/api',
        target: 'https://ukebook.immerdieses.de/api'
      }]
    }));
});

gulp.task('webserver', function() {
    return gulp.src('src')
        .pipe(webserver({
            livereload: true,
            port: 8500,
            host: 'localhost',        //rich from network
            //fallback: 'index.html', //for spa
            open:  "http://localhost:8500/wigeoweb",
            proxies: [
                {
                    source: '/wigeoweb-dev',
                    target: 'http://wigeowebhotd:11711/wigeoweb'
                },{
                    source: '/cache',
                    target: 'http://wigeowebhotd:11711/cache'
                },{
                    source: '/icons',
                    target: 'http://wigeowebhotd:11711/icons'
                },
                {
                    source: '/wigeoweb-claas',
                    target: 'http://192.168.123.77:10080/wigeoweb-claas'
                }
            ]
        }));
});

// watch for changes and run the relevant task
gulp.task('watch', function () {
  gulp.watch('src/**/*.js', ['js']);
  gulp.watch('src/**/*.html', ['html']);
  gulp.watch('styles/**/*.css', ['css']);
  gulp.watch('styles/**/*.scss', ['sass']);
  gulp.watch('src/**/*.tab', ['tab']);
});

// move dependencies into build dir
gulp.task('dependencies', function () {
  return gulp.src([
      'node_modules/jquery/dist/jquery.min.js',
      'node_modules/angular/angular.min.js',
      'node_modules/angular-animate/angular-animate.js',
      'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'node_modules/angular-local-storage/dist/angular-local-storage.min.js',
      'node_modules/angular-messages/angular-messages.min.js',
      'node_modules/angular-resource/angular-resource.min.js',
      'node_modules/angular-route/angular-route.min.js',
      'node_modules/angular-sanitize/angular-sanitize.min.js',
      'node_modules/angular-touch/angular-touch.min.js',
      'node_modules/angular-cookies/angular-cookies.min.js',
      'node_modules/bootstrap/dist/js/bootstrap.min.js',
      'node_modules/q/q.js',
      'node_modules/angular-route-segment/build/angular-route-segment.js',
      'node_modules/gulp-traceur/node_modules/traceur/bin/traceur-runtime.js',
      'scriptasaurus/ukeGeeks.scriptasaurus.merged.js',
      'columnizer/src/jquery.columnizer.min.js'
    ])
    .pipe(gulp.dest('build/lib'));
});

// transpile & move js
gulp.task('js', function () {
  return gulp.src('src/**/*.js')
    .pipe(rename({
      extname: '.js'
    }))
    .pipe(traceur({
      types: true,
      memberVariables: true
    }))
    .pipe(rename({
      extname: '.js'
    }))
    .pipe(gulp.dest('build'));
});

// move html
gulp.task('html', function () {
  return gulp.src('src/**/*.html')
    .pipe(gulp.dest('build'))
});

// move test html
gulp.task('testhtml', function () {
  return gulp.src('test/**/*.html')
    .pipe(gulp.dest('build'))
});

// move css
gulp.task('css', function () {
  return gulp.src(['src/**/*.css','scriptasaurus/*.css'])
    .pipe(gulp.dest('build/css'))
});

// move fonts
gulp.task('fonts', function () {
  return gulp.src(['node_modules/bootstrap/fonts/*.*'])
    .pipe(gulp.dest('build/fonts/bootstrap'))
});

// move tabulatures
gulp.task('tab', function () {
  return gulp.src('src/**/*.tab')
    .pipe(gulp.dest('build/tabs'))
});

// sass
gulp.task('sass', function () {
  gulp.src('styles/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('build/css'));
});
<commit_msg>added missing directive file<commit_after>var gulp = require('gulp'),
  concat = require('gulp-concat'),
  sass = require('gulp-sass'),
  rename = require('gulp-rename'),
  shell = require('gulp-shell'),
  traceur = require('gulp-traceur'),
  webserver = require('gulp-webserver');

// run init tasks
gulp.task('default', ['dependencies', 'js', 'html', 'sass', 'tab', 'fonts', 'css']);

// run development task
gulp.task('dev', ['testhtml', 'watch', 'serve']);

// serve the build dir
gulp.task('serve-prod', function () {
  gulp.src('build')
    .pipe(webserver({
      open: true,
      livereload: true,
      host: 'localhost',
      proxies: [{
        source: '/api',
        target: 'https://ukebook.immerdieses.de/api'
      }]
    }));
});

// watch for changes and run the relevant task
gulp.task('watch', function () {
  gulp.watch('src/**/*.js', ['js']);
  gulp.watch('src/**/*.html', ['html']);
  gulp.watch('styles/**/*.css', ['css']);
  gulp.watch('styles/**/*.scss', ['sass']);
  gulp.watch('src/**/*.tab', ['tab']);
});

// move dependencies into build dir
gulp.task('dependencies', function () {
  return gulp.src([
      'node_modules/jquery/dist/jquery.min.js',
      'node_modules/angular/angular.min.js',
      'node_modules/angular-animate/angular-animate.js',
      'node_modules/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'node_modules/angular-local-storage/dist/angular-local-storage.min.js',
      'node_modules/angular-messages/angular-messages.min.js',
      'node_modules/angular-resource/angular-resource.min.js',
      'node_modules/angular-route/angular-route.min.js',
      'node_modules/angular-sanitize/angular-sanitize.min.js',
      'node_modules/angular-touch/angular-touch.min.js',
      'node_modules/angular-cookies/angular-cookies.min.js',
      'node_modules/bootstrap/dist/js/bootstrap.min.js',
      'node_modules/q/q.js',
      'node_modules/angular-route-segment/build/angular-route-segment.js',
      'node_modules/gulp-traceur/node_modules/traceur/bin/traceur-runtime.js',
      'scriptasaurus/ukeGeeks.scriptasaurus.merged.js',
      'columnizer/src/jquery.columnizer.min.js'
    ])
    .pipe(gulp.dest('build/lib'));
});

// transpile & move js
gulp.task('js', function () {
  return gulp.src('src/**/*.js')
    .pipe(rename({
      extname: '.js'
    }))
    .pipe(traceur({
      types: true,
      memberVariables: true
    }))
    .pipe(rename({
      extname: '.js'
    }))
    .pipe(gulp.dest('build'));
});

// move html
gulp.task('html', function () {
  return gulp.src('src/**/*.html')
    .pipe(gulp.dest('build'))
});

// move test html
gulp.task('testhtml', function () {
  return gulp.src('test/**/*.html')
    .pipe(gulp.dest('build'))
});

// move css
gulp.task('css', function () {
  return gulp.src(['src/**/*.css','scriptasaurus/*.css'])
    .pipe(gulp.dest('build/css'))
});

// move fonts
gulp.task('fonts', function () {
  return gulp.src(['node_modules/bootstrap/fonts/*.*'])
    .pipe(gulp.dest('build/fonts/bootstrap'))
});

// move tabulatures
gulp.task('tab', function () {
  return gulp.src('src/**/*.tab')
    .pipe(gulp.dest('build/tabs'))
});

// sass
gulp.task('sass', function () {
  gulp.src('styles/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('build/css'));
});
<|endoftext|>


<commit_before>'use strict';

const Promise = require('bluebird');
const anymatch = require('anymatch');
const Path = require('path');
const co = require('co');
const _ = require('lodash');
const fs = Promise.promisifyAll(require('fs-extra'));
const Log = require('../core/log');
const mix = require('../core/mixins/mix');
const Emitter = require('../core/mixins/emitter');

module.exports = class Builder extends mix(Emitter) {

    constructor(theme, engine, config, app) {
        super(app);

        this._app = app;
        this._engine = engine;
        this._config = config;
        this._theme = theme;

        this._static = [];
        this._requests = [];

        this._throttle = require('throat')(Promise)(config.concurrency || 100);

        this._init();
    }

    /*
     * Deprecated. Use start() instead.
     */
    build() {
        return this.start();
    }

    start() {

        this._validate();
        this._reset();

        // Make sure the sources have loaded
        return this._app.load().then(() => {

            this._buildRequests();

            this.emit('start');

            // remove and recreate build dir
            const setup = fs.removeAsync(this._config.dest).then(() => fs.ensureDirAsync(this._config.dest));

            return setup.then(() => {

                let jobs = [];

                // 1. Start any static copy jobs
                jobs.push(this._static.map(p => this._copy(p.path, Path.join(Path.sep, p.mount))));

                // 2. Run the requests in parallel
                this._requests.forEach(r => {
                    let req = this._onRequest(r);
                    if (req) {
                        this._jobsCount++;
                        jobs.push(req);
                    }
                });

                return Promise.all(_.flatten(jobs));
            });

        }).then(() => {
            const stats = {
                errorCount: this._errorCount,
            };
            this.emit('end', stats);
            return stats;
        }).catch(e => {
            this.emit('error', e);
            throw e;
        });
    }

    stop() {
        // can we stop it once running?
    }

    use() {

    }

    _reset() {
        this._errorCount = 0;
        this._jobsCount = 0;
        this._progressCount = 0;
    }

    _init() {
        this._static = this._static.concat(this._theme.static());
        this._engine.setGlobal('env', {
            builder: true,
        });
    }

    _buildRequests() {
        const routes = this._theme.routes();
        const resolvers = _.isFunction(this._theme.resolvers) ? this._theme.resolvers() : this._getLegacyResolvers(routes);
        let requests = [];

        _.forEach(resolvers, (routeResolvers, handle) => {
            const route = _.find(routes, {handle: handle});

            if (!route) {
                Log.debug(`No route found for handle '${handle}'`);
                return;
            }

            for (let resolver of routeResolvers) {
                let resolverSet = _.isFunction(resolver) ? resolver(this._app) : [].concat(resolver);
                for (const params of resolverSet) {
                    const url = this._theme.urlFromRoute(route.handle, params, true);
                    const req = Builder.Request(url, params, route);
                    this._requests.push(req);
                }
            }

        });
    }

    _getLegacyResolvers(routes) {
        let resolvers = {};
        for (let route of routes) {
            _.set(resolvers, route.handle, [].concat(route.params || null));
        }
        return resolvers;
    }

    _onRequest(req) {

        if (req.route.redirect && this._theme.redirectView) {
            req.route.context = {
                redirectUrl: req.route.redirect
            };
            req.route.view = this._theme.redirectView();
        }

        if (req.route.static) {
            const staticPath = _.isFunction(req.route.static) ? req.route.static(req.params, this._app) : req.route.static;
            return this._copy(unescape(staticPath), unescape(req.url));
        }

        if (req.route.view) {
            const ext = this._app.web.get('builder.ext');
            const dest = req.url + (req.url == '/' ? `index${ext}` : ext);
            const context = req.route.context || {};

            context.request = req;
            context.renderEnv = {
                request: context.request,
                builder: true,
                server: false,
            };

            return this._render(req.route.view, context)
                    .then(contents => this._write(contents, dest) )
                    .then(() => {
                        this.emit('exported', req);
                        Log.debug(`Exported '${req.url}' ==> '${dest}'`);
                        this._updateProgress();
                    })
                    .catch(err => this._onError(err, req, dest));
        }

    }

    _onError(err, req, dest) {
        this._errorCount++;
        this._updateProgress();
        this.emit('error', new Error(`Failed to export url ${req.url} - ${err.message}`));
        return this._render(this._theme.errorView(), { error: err }).then(contents => this._write(contents, dest)).catch(err => {
            this.emit('error', err);
        });
    }

    _render(view, context) {
        if (_.isFunction(view)) {
            return this._engine.renderString(view(), context);
        } else {
            return this._engine.render(view, context);
        }
    }

    _copy(source, dest, addToJobCount = true) {
        let ignored = this._config.static.ignored;
        dest = _.trimEnd(Path.join(this._config.dest, dest), Path.sep);
        source = Path.resolve(source);
        if (addToJobCount) {
            this._jobsCount++;
        }
        return fs.copyAsync(source, dest, {
            clobber: true,
            filter: function(path){
                return ! anymatch(ignored, path);
            }
        }).then(() => {
            this._updateProgress();
            Log.debug(`Copied '${source}' ==> '${dest}'`);
        }).catch(e => {
            this._updateProgress();
            Log.error(`Error copying '${source}' ==> '${dest}'`);
            this._errorCount++;
        });
    }

    _write(contents, dest) {
        dest = _.trimEnd(Path.join(this._config.dest, dest), Path.sep);
        return fs.ensureDirAsync(Path.parse(dest).dir).then(() => fs.writeFileAsync(dest, contents));
    }

    _updateProgress() {
        this._progressCount++;
        this.emit('progress', this._progressCount, this._jobsCount);
    }

    _validate() {
        if (!this._config.dest) {
            throw new Error('You need to specify a build destination in your configuration.');
        }
        for (const stat of this._theme.static()) {
            if (stat.path == this._config.dest) {
                throw new Error(`Your build destination directory (${Path.resolve(stat.path)}) cannot be the same as any of your static assets directories.`);
            }
        }
    }

    static Request(url, params, route) {
        route = _.clone(route);
        return {
            headers: {},
            query: {},
            url: url,
            segments: _.compact(url.split('/')),
            params: params,
            path: url,
            error: null,
            errorStatus: null,
            route: route,
        };
    }

};
<commit_msg>Compat fix<commit_after>'use strict';

const Promise = require('bluebird');
const anymatch = require('anymatch');
const Path = require('path');
const co = require('co');
const _ = require('lodash');
const fs = Promise.promisifyAll(require('fs-extra'));
const Log = require('../core/log');
const mix = require('../core/mixins/mix');
const Emitter = require('../core/mixins/emitter');

module.exports = class Builder extends mix(Emitter) {

    constructor(theme, engine, config, app) {
        super(app);

        this._app = app;
        this._engine = engine;
        this._config = config;
        this._theme = theme;

        this._static = [];
        this._requests = [];

        this._throttle = require('throat')(Promise)(config.concurrency || 100);

        this._init();
    }

    /*
     * Deprecated. Use start() instead.
     */
    build() {
        return this.start();
    }

    start() {

        this._validate();
        this._reset();

        // Make sure the sources have loaded
        return this._app.load().then(() => {

            this._buildRequests();

            this.emit('start');

            // remove and recreate build dir
            const setup = fs.removeAsync(this._config.dest).then(() => fs.ensureDirAsync(this._config.dest));

            return setup.then(() => {

                let jobs = [];

                // 1. Start any static copy jobs
                jobs.push(this._static.map(p => this._copy(p.path, Path.join(Path.sep, p.mount))));

                // 2. Run the requests in parallel
                this._requests.forEach(r => {
                    let req = this._onRequest(r);
                    if (req) {
                        this._jobsCount++;
                        jobs.push(req);
                    }
                });

                return Promise.all(_.flatten(jobs));
            });

        }).then(() => {
            const stats = {
                errorCount: this._errorCount,
            };
            this.emit('end', stats);
            return stats;
        }).catch(e => {
            this.emit('error', e);
            throw e;
        });
    }

    stop() {
        // can we stop it once running?
    }

    use() {

    }

    _reset() {
        this._errorCount = 0;
        this._jobsCount = 0;
        this._progressCount = 0;
    }

    _init() {
        this._static = this._static.concat(this._theme.static());
        this._engine.setGlobal('env', {
            builder: true,
        });
    }

    _buildRequests() {
        const routes = this._theme.routes();
        const resolvers = _.isFunction(this._theme.resolvers) ? this._theme.resolvers() : this._getLegacyResolvers(routes);
        let requests = [];

        _.forEach(resolvers, (routeResolvers, handle) => {
            const route = _.find(routes, {handle: handle});

            if (!route) {
                Log.debug(`No route found for handle '${handle}'`);
                return;
            }

            for (let resolver of routeResolvers) {
                let resolverSet = _.isFunction(resolver) ? resolver(this._app) : [].concat(resolver);
                for (const params of resolverSet) {
                    const url = this._theme.urlFromRoute(route.handle, params, true);
                    const req = Builder.Request(url, params, route);
                    this._requests.push(req);
                }
            }

        });
    }

    _getLegacyResolvers(routes) {
        let resolvers = {};
        for (let route of routes) {
            _.set(resolvers, route.handle, [].concat(route.params || null));
        }
        return resolvers;
    }

    _onRequest(req) {

        if (req.route.redirect && this._theme.redirectView) {
            req.route.context = {
                redirectUrl: req.route.redirect
            };
            req.route.view = this._theme.redirectView();
        }

        if (req.route.static) {
            const staticPath = _.isFunction(req.route.static) ? req.route.static(req.params, this._app) : req.route.static;
            return this._copy(unescape(staticPath), unescape(req.url), false);
        }

        if (req.route.view) {
            const ext = this._app.web.get('builder.ext');
            const dest = req.url + (req.url == '/' ? `index${ext}` : ext);
            const context = req.route.context || {};

            context.request = req;
            context.renderEnv = {
                request: context.request,
                builder: true,
                server: false,
            };

            return this._render(req.route.view, context)
                    .then(contents => this._write(contents, dest) )
                    .then(() => {
                        this.emit('exported', req);
                        Log.debug(`Exported '${req.url}' ==> '${dest}'`);
                        this._updateProgress();
                    })
                    .catch(err => this._onError(err, req, dest));
        }

    }

    _onError(err, req, dest) {
        this._errorCount++;
        this._updateProgress();
        this.emit('error', new Error(`Failed to export url ${req.url} - ${err.message}`));
        return this._render(this._theme.errorView(), { error: err }).then(contents => this._write(contents, dest)).catch(err => {
            this.emit('error', err);
        });
    }

    _render(view, context) {
        if (_.isFunction(view)) {
            return this._engine.renderString(view(), context);
        } else {
            return this._engine.render(view, context);
        }
    }

    _copy(source, dest, addToJobCount) {
        let ignored = this._config.static.ignored;
        dest = _.trimEnd(Path.join(this._config.dest, dest), Path.sep);
        source = Path.resolve(source);
        if (addToJobCount !== false) {
            this._jobsCount++;
        }
        return fs.copyAsync(source, dest, {
            clobber: true,
            filter: function(path){
                return ! anymatch(ignored, path);
            }
        }).then(() => {
            this._updateProgress();
            Log.debug(`Copied '${source}' ==> '${dest}'`);
        }).catch(e => {
            this._updateProgress();
            Log.error(`Error copying '${source}' ==> '${dest}'`);
            this._errorCount++;
        });
    }

    _write(contents, dest) {
        dest = _.trimEnd(Path.join(this._config.dest, dest), Path.sep);
        return fs.ensureDirAsync(Path.parse(dest).dir).then(() => fs.writeFileAsync(dest, contents));
    }

    _updateProgress() {
        this._progressCount++;
        this.emit('progress', this._progressCount, this._jobsCount);
    }

    _validate() {
        if (!this._config.dest) {
            throw new Error('You need to specify a build destination in your configuration.');
        }
        for (const stat of this._theme.static()) {
            if (stat.path == this._config.dest) {
                throw new Error(`Your build destination directory (${Path.resolve(stat.path)}) cannot be the same as any of your static assets directories.`);
            }
        }
    }

    static Request(url, params, route) {
        route = _.clone(route);
        return {
            headers: {},
            query: {},
            url: url,
            segments: _.compact(url.split('/')),
            params: params,
            path: url,
            error: null,
            errorStatus: null,
            route: route,
        };
    }

};
<|endoftext|>