<commit_before>define([
    'jquery',
    'underscore',
    'knockout',
    'viewmodels/card',
    'arches',
    'graph-designer-data',
    'bindings/sortable',
    'bindings/scrollTo',
    'widgets',
    'card-components'
], function($, _, ko, CardViewModel, arches, data) {
    var CardTreeViewModel = function(params) {
        var self = this;
        var filter = ko.observable('');
        var loading = ko.observable(false);
        self.multiselect = params.multiselect || false;
        var selection;
        if (params.multiselect) {
            selection = ko.observableArray([]);
        } else {
            selection = ko.observable();
        }
        var hover = ko.observable();
        var scrollTo = ko.observable();
        var cachedFlatTree;

        this.flattenTree = function(parents, flatList) {
            _.each(ko.unwrap(parents), function(parent) {
                flatList.push(parent);
                self.flattenTree(
                    ko.unwrap(parent.cards),
                    flatList
                );
            }, this);
            return flatList;
        };

        this.updateNodeList = function() {
            if (self.cachedFlatTree === undefined) {
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
            }
        };

        var toggleAll = function(state) {
            self.updateNodeList();
            _.each(self.cachedFlatTree, function(node) {
                node.expanded(state);
            });
            if (state) {
                self.rootExpanded(true);
            }
        };

        var selectAll = function(state) {
            self.updateNodeList();
            _.each(self.cachedFlatTree, function(node) {
                if (node.selected() !== state) {
                    node.selected(state);
                }
            });
        };

        var expandToRoot = function(node) {
            //expands all nodes up to the root, but does not expand the root.
            self.updateNodeList();
            if (node.parent) {
                node.parent.expanded(true);
                expandToRoot(node.parent);
            } else {
                node.expanded(true);
                _.each(self.cachedFlatTree, function(n) {
                    if (node.parentnodegroup_id !== null && node.parentnodegroup_id === n.nodegroupid) {
                        expandToRoot(n);
                    }
                });
            }
        };

        var removeCard = function(cards, nodegroupid) {
            var removed;
            _.each(cards(), function(card){
                if (card.nodegroupid === nodegroupid) {
                    cards.remove(card);
                    removed = card;
                } else {
                    removeCard(card.cards, nodegroupid);
                }
            });
            return removed;
        };

        var createLookup = function(list, idKey) {
            return _.reduce(list, function(lookup, item) {
                lookup[ko.unwrap(item[idKey])] = item;
                return lookup;
            }, {});
        };

        _.extend(this, {
            filterEnterKeyHandler: function(context, e) {
                if (e.keyCode === 13) {
                    var highlightedItems = _.filter(self.flattenTree(self.topCards(), []), function(item) {
                        return item.highlight && item.highlight();
                    });
                    var previousItem = scrollTo();
                    scrollTo(null);
                    if (highlightedItems.length > 0) {
                        var scrollIndex = 0;
                        var previousIndex = highlightedItems.indexOf(previousItem);
                        if (previousItem && highlightedItems[previousIndex+1]) {
                            scrollIndex = previousIndex + 1;
                        }
                        scrollTo(highlightedItems[scrollIndex]);
                    }
                    return false;
                }
                return true;
            },
            loading: loading,
            cachedFlatTree: cachedFlatTree,
            widgetLookup: createLookup(data.widgets, 'widgetid'),
            cardComponentLookup: createLookup(data.cardComponents, 'componentid'),
            nodeLookup: createLookup(params.graphModel.get('nodes')(), 'nodeid'),
            graphid: params.graph.graphid,
            graphname: params.graph.name,
            graphiconclass: params.graph.iconclass,
            graph: params.graph,
            graphModel: params.graphModel,
            expandAll: function() {
                toggleAll(true);
            },
            collapseAll: function() {
                toggleAll(false);
            },
            selectAllCards: function() {
                selectAll(true);
            },
            clearSelection: function() {
                selectAll(false);
            },
            expandToRoot: expandToRoot,
            rootExpanded: ko.observable(true),
            on: function() {
                return;
            },
            topCards: ko.observableArray(_.filter(data.cards, function(card) {
                var nodegroup = _.find(ko.unwrap(params.graphModel.get('nodegroups')), function(group) {
                    return ko.unwrap(group.nodegroupid) === card.nodegroup_id;
                });
                return !nodegroup || !ko.unwrap(nodegroup.parentnodegroup_id);
            }).map(function(card) {
                return new CardViewModel({
                    card: card,
                    graphModel: params.graphModel,
                    tile: null,
                    resourceId: ko.observable(),
                    displayname: ko.observable(),
                    handlers: {},
                    cards: data.cards,
                    tiles: [],
                    selection: selection,
                    hover: hover,
                    scrollTo: scrollTo,
                    multiselect: self.multiselect,
                    loading: loading,
                    filter: filter,
                    provisionalTileViewModel: null,
                    cardwidgets: data.cardwidgets,
                    userisreviewer: true,
                    perms: ko.observableArray(),
                    permsLiteral: ko.observableArray()
                });
            })),
            beforeMove: function(e) {
                e.cancelDrop = (e.sourceParent!==e.targetParent);
            },
            updateCards: function(selectedNodegroupId, data) {
                var cards = data.cards;
                var nodegroups = data.nodegroups;
                var existingNodegroupIds = _.pluck(self.graphModel.get('nodegroups'), 'nodegroupid');
                var newNodegroups = _.filter(nodegroups, function(ng) {return _.contains(existingNodegroupIds, ng.nodegroupid) === false;});
                self.topCards.removeAll();
                if (newNodegroups.length > 0) {
                    self.graphModel.set('nodegroups', self.graphModel.get('nodegroups').concat(newNodegroups));
                }
                self.topCards(_.filter(cards, function(card) {
                    var nodegroup = _.find(ko.unwrap(self.graphModel.get('nodegroups')), function(group) {
                        return ko.unwrap(group.nodegroupid) === card.nodegroup_id;
                    });
                    if (nodegroup) {
                        return !nodegroup || !ko.unwrap(nodegroup.parentnodegroup_id);
                    }
                }, self).map(function(card) {
                    var newCard = new CardViewModel({
                        card: card,
                        graphModel: self.graphModel,
                        tile: null,
                        resourceId: ko.observable(),
                        displayname: ko.observable(),
                        handlers: {},
                        cards: cards,
                        tiles: [],
                        selection: selection,
                        hover: hover,
                        scrollTo: scrollTo,
                        multiselect: self.multiselect,
                        loading: loading,
                        filter: filter,
                        provisionalTileViewModel: null,
                        cardwidgets: data.cardwidgets,
                        userisreviewer: true,
                        perms: ko.observableArray(),
                        permsLiteral: ko.observableArray()
                    });
                    return newCard;
                }, self));
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
                _.each(self.cachedFlatTree, function(node) {
                    if (node.nodegroupid === selectedNodegroupId) {
                        self.collapseAll();
                        self.multiselect ? self.selection([node]) : self.selection(node);
                        self.expandToRoot(node);
                    }
                });
            },
            deleteCard: function(selectedNodegroupId) {
                removeCard(self.topCards, selectedNodegroupId);
                if (self.topCards().length){ self.topCards()[0].selected(true); }
            },
            addCard: function(data) {
                var cards;
                self.graphModel.set('nodegroups', self.graphModel.get('nodegroups').concat([data.nodegroup]));
                self.topCards.push(new CardViewModel({
                    card: data.card,
                    graphModel: self.graphModel,
                    tile: null,
                    resourceId: ko.observable(),
                    displayname: ko.observable(),
                    handlers: {},
                    cards: cards,
                    tiles: [],
                    selection: selection,
                    hover: hover,
                    scrollTo: scrollTo,
                    multiselect: self.multiselect,
                    loading: loading,
                    filter: filter,
                    provisionalTileViewModel: null,
                    cardwidgets: data.cardwidgets,
                    userisreviewer: true,
                    perms: ko.observableArray(),
                    permsLiteral: ko.observableArray()
                }));
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
            },
            reorderCards: function() {
                loading(true);
                var cards = _.map(self.topCards(), function(card, i) {
                    card.model.get('sortorder')(i);
                    return {
                        id: card.model.id,
                        name: card.model.get('name')(),
                        sortorder: i
                    };
                });
                $.ajax({
                    type: 'POST',
                    data: JSON.stringify({
                        cards: cards
                    }),
                    url: arches.urls.reorder_cards,
                    complete: function() {
                        loading(false);
                    }
                });
            },
            selection: selection,
            filter: filter
        });
        var topCard = self.topCards()[0];
        if (topCard != null) {
            if (self.multiselect === true) {
                selection.push(topCard.tiles().length > 0 ? topCard.tiles()[0] : topCard);
            } else {
                selection(topCard.tiles().length > 0 ? topCard.tiles()[0] : topCard);
            }
        }
    };
    return CardTreeViewModel;
});
<commit_msg>Handles for undefined card error. re #3906<commit_after>define([
    'jquery',
    'underscore',
    'knockout',
    'viewmodels/card',
    'arches',
    'graph-designer-data',
    'bindings/sortable',
    'bindings/scrollTo',
    'widgets',
    'card-components'
], function($, _, ko, CardViewModel, arches, data) {
    var CardTreeViewModel = function(params) {
        var self = this;
        var filter = ko.observable('');
        var loading = ko.observable(false);
        self.multiselect = params.multiselect || false;
        var selection;
        if (params.multiselect) {
            selection = ko.observableArray([]);
        } else {
            selection = ko.observable();
        }
        var hover = ko.observable();
        var scrollTo = ko.observable();
        var cachedFlatTree;

        this.flattenTree = function(parents, flatList) {
            _.each(ko.unwrap(parents), function(parent) {
                flatList.push(parent);
                self.flattenTree(
                    ko.unwrap(parent.cards),
                    flatList
                );
            }, this);
            return flatList;
        };

        this.updateNodeList = function() {
            if (self.cachedFlatTree === undefined) {
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
            }
        };

        var toggleAll = function(state) {
            self.updateNodeList();
            _.each(self.cachedFlatTree, function(node) {
                node.expanded(state);
            });
            if (state) {
                self.rootExpanded(true);
            }
        };

        var selectAll = function(state) {
            self.updateNodeList();
            _.each(self.cachedFlatTree, function(node) {
                if (node.selected() !== state) {
                    node.selected(state);
                }
            });
        };

        var expandToRoot = function(node) {
            //expands all nodes up to the root, but does not expand the root.
            self.updateNodeList();
            if (node.parent) {
                node.parent.expanded(true);
                expandToRoot(node.parent);
            } else {
                node.expanded(true);
                _.each(self.cachedFlatTree, function(n) {
                    if (node.parentnodegroup_id !== null && node.parentnodegroup_id === n.nodegroupid) {
                        expandToRoot(n);
                    }
                });
            }
        };

        var removeCard = function(cards, nodegroupid) {
            var removed;
            _.each(cards(), function(card){
                if (card) {
                    if (card.nodegroupid === nodegroupid) {
                        cards.remove(card);
                        removed = card;
                    } else {
                        removeCard(card.cards, nodegroupid);
                    }
                }
            });
            return removed;
        };

        var createLookup = function(list, idKey) {
            return _.reduce(list, function(lookup, item) {
                lookup[ko.unwrap(item[idKey])] = item;
                return lookup;
            }, {});
        };

        _.extend(this, {
            filterEnterKeyHandler: function(context, e) {
                if (e.keyCode === 13) {
                    var highlightedItems = _.filter(self.flattenTree(self.topCards(), []), function(item) {
                        return item.highlight && item.highlight();
                    });
                    var previousItem = scrollTo();
                    scrollTo(null);
                    if (highlightedItems.length > 0) {
                        var scrollIndex = 0;
                        var previousIndex = highlightedItems.indexOf(previousItem);
                        if (previousItem && highlightedItems[previousIndex+1]) {
                            scrollIndex = previousIndex + 1;
                        }
                        scrollTo(highlightedItems[scrollIndex]);
                    }
                    return false;
                }
                return true;
            },
            loading: loading,
            cachedFlatTree: cachedFlatTree,
            widgetLookup: createLookup(data.widgets, 'widgetid'),
            cardComponentLookup: createLookup(data.cardComponents, 'componentid'),
            nodeLookup: createLookup(params.graphModel.get('nodes')(), 'nodeid'),
            graphid: params.graph.graphid,
            graphname: params.graph.name,
            graphiconclass: params.graph.iconclass,
            graph: params.graph,
            graphModel: params.graphModel,
            expandAll: function() {
                toggleAll(true);
            },
            collapseAll: function() {
                toggleAll(false);
            },
            selectAllCards: function() {
                selectAll(true);
            },
            clearSelection: function() {
                selectAll(false);
            },
            expandToRoot: expandToRoot,
            rootExpanded: ko.observable(true),
            on: function() {
                return;
            },
            topCards: ko.observableArray(_.filter(data.cards, function(card) {
                var nodegroup = _.find(ko.unwrap(params.graphModel.get('nodegroups')), function(group) {
                    return ko.unwrap(group.nodegroupid) === card.nodegroup_id;
                });
                return !nodegroup || !ko.unwrap(nodegroup.parentnodegroup_id);
            }).map(function(card) {
                return new CardViewModel({
                    card: card,
                    graphModel: params.graphModel,
                    tile: null,
                    resourceId: ko.observable(),
                    displayname: ko.observable(),
                    handlers: {},
                    cards: data.cards,
                    tiles: [],
                    selection: selection,
                    hover: hover,
                    scrollTo: scrollTo,
                    multiselect: self.multiselect,
                    loading: loading,
                    filter: filter,
                    provisionalTileViewModel: null,
                    cardwidgets: data.cardwidgets,
                    userisreviewer: true,
                    perms: ko.observableArray(),
                    permsLiteral: ko.observableArray()
                });
            })),
            beforeMove: function(e) {
                e.cancelDrop = (e.sourceParent!==e.targetParent);
            },
            updateCards: function(selectedNodegroupId, data) {
                var cards = data.cards;
                var nodegroups = data.nodegroups;
                var existingNodegroupIds = _.pluck(self.graphModel.get('nodegroups'), 'nodegroupid');
                var newNodegroups = _.filter(nodegroups, function(ng) {return _.contains(existingNodegroupIds, ng.nodegroupid) === false;});
                self.topCards.removeAll();
                if (newNodegroups.length > 0) {
                    self.graphModel.set('nodegroups', self.graphModel.get('nodegroups').concat(newNodegroups));
                }
                self.topCards(_.filter(cards, function(card) {
                    var nodegroup = _.find(ko.unwrap(self.graphModel.get('nodegroups')), function(group) {
                        return ko.unwrap(group.nodegroupid) === card.nodegroup_id;
                    });
                    if (nodegroup) {
                        return !nodegroup || !ko.unwrap(nodegroup.parentnodegroup_id);
                    }
                }, self).map(function(card) {
                    var newCard = new CardViewModel({
                        card: card,
                        graphModel: self.graphModel,
                        tile: null,
                        resourceId: ko.observable(),
                        displayname: ko.observable(),
                        handlers: {},
                        cards: cards,
                        tiles: [],
                        selection: selection,
                        hover: hover,
                        scrollTo: scrollTo,
                        multiselect: self.multiselect,
                        loading: loading,
                        filter: filter,
                        provisionalTileViewModel: null,
                        cardwidgets: data.cardwidgets,
                        userisreviewer: true,
                        perms: ko.observableArray(),
                        permsLiteral: ko.observableArray()
                    });
                    return newCard;
                }, self));
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
                _.each(self.cachedFlatTree, function(node) {
                    if (node.nodegroupid === selectedNodegroupId) {
                        self.collapseAll();
                        self.multiselect ? self.selection([node]) : self.selection(node);
                        self.expandToRoot(node);
                    }
                });
            },
            deleteCard: function(selectedNodegroupId) {
                removeCard(self.topCards, selectedNodegroupId);
                if (self.topCards().length){ self.topCards()[0].selected(true); }
            },
            addCard: function(data) {
                var cards;
                self.graphModel.set('nodegroups', self.graphModel.get('nodegroups').concat([data.nodegroup]));
                self.topCards.push(new CardViewModel({
                    card: data.card,
                    graphModel: self.graphModel,
                    tile: null,
                    resourceId: ko.observable(),
                    displayname: ko.observable(),
                    handlers: {},
                    cards: cards,
                    tiles: [],
                    selection: selection,
                    hover: hover,
                    scrollTo: scrollTo,
                    multiselect: self.multiselect,
                    loading: loading,
                    filter: filter,
                    provisionalTileViewModel: null,
                    cardwidgets: data.cardwidgets,
                    userisreviewer: true,
                    perms: ko.observableArray(),
                    permsLiteral: ko.observableArray()
                }));
                self.cachedFlatTree = self.flattenTree(self.topCards(), []);
            },
            reorderCards: function() {
                loading(true);
                var cards = _.map(self.topCards(), function(card, i) {
                    card.model.get('sortorder')(i);
                    return {
                        id: card.model.id,
                        name: card.model.get('name')(),
                        sortorder: i
                    };
                });
                $.ajax({
                    type: 'POST',
                    data: JSON.stringify({
                        cards: cards
                    }),
                    url: arches.urls.reorder_cards,
                    complete: function() {
                        loading(false);
                    }
                });
            },
            selection: selection,
            filter: filter
        });
        var topCard = self.topCards()[0];
        if (topCard != null) {
            if (self.multiselect === true) {
                selection.push(topCard.tiles().length > 0 ? topCard.tiles()[0] : topCard);
            } else {
                selection(topCard.tiles().length > 0 ? topCard.tiles()[0] : topCard);
            }
        }
    };
    return CardTreeViewModel;
});
<|endoftext|>


<commit_before>var _ = require('underscore');
var qs = require('querystring');
var tm = require('./lib/tm');
var source = require('./lib/source');
var project = require('./lib/project');
var express = require('express');
var argv = require('optimist')
    .config('config')
    .argv;

var app = express();
app.use(express.bodyParser());
app.use(app.router);
app.use('/app', express.static(__dirname + '/app', { maxAge:3600e3 }));
app.use('/ext', express.static(__dirname + '/ext', { maxAge:3600e3 }));

app.param('project', function(req, res, next) {
    if (req.method === 'PUT' && req.body._recache && req.query.id) {
        source.invalidate(req.query.id, next);
    } else {
        next();
    }
}, function(req, res, next) {
    var id = req.query.id;
    var tmp = id && tm.tmpid(id);
    if (req.method === 'PUT') {
        var data = req.body;
    } else if (tmp && req.path === '/project') {
        var data = {
            styles: { 'style.mss': 'Map {\n  background-color:#fff;\n}\n\n#water {\n  polygon-fill:#b8dee6;\n}\n\n#road {\n  line-color:#ccc;\n}\n\n#admin {\n  line-color:rgba(0,32,64,0.2);\n}' },
            sources: ['mbstreets']
        };
    } else {
        var data = false;
    }
    project({
        id: id,
        data: data,
        perm: !tmp && !!data
    }, function(err, project) {
        if (err && !tmp) tm.history(id, true);
        if (err) return next(err);

        if (!tmp) tm.history(id);
        req.project = project;
        req.project.data._tmp = tmp;
        return next();
    });
});

app.param('history', function(req, res, next) {
    req.history = {};
    var history = tm.history().concat([]); // Clone.

    var load = function() {
        if (!history.length) return next();
        var id = history.shift();
        project.info(id, function(err, info) {
            if (err) {
                tm.history(id, true);
            } else {
                req.history[id] = info;
            }
            load();
        });
    };
    load();
});

app.put('/:project(project)', function(req, res, next) {
    res.send({
        _recache:false,
        mtime:req.project.data.mtime,
        background:req.project.data.background
    });
});

app.get('/:project(project)', function(req, res, next) {
    res.set({'content-type':'text/html'});
    return res.send(tm.templates.project({
        cartoRef: require('carto').tree.Reference.data,
        sources: [req.project._backend.data],
        library: _(source.library).filter(function(source, s) {
            return !_(req.project.data.sources).include(s);
        }),
        project: req.project.data
    }));
});

app.get('/:project(project)/:z/:x/:y.png', function(req, res, next) {
    var z = req.params.z | 0;
    var x = req.params.x | 0;
    var y = req.params.y | 0;
    req.project.getTile(z,x,y, function(err, data, headers) {
        if (err && err.message === 'Tilesource not loaded') {
            return res.redirect(req.path);
        } else if (err) {
            return next(err);
        }
        headers['cache-control'] = 'max-age=3600';
        res.set(headers);
        return res.send(data);
    });
});

app.get('/thumb.png', function(req, res, next) {
    if (!req.query.id) return next(new Error('No id specified'));
    project.thumb({id:req.query.id}, function(err, thumb) {
        if (err) return next(err);
        res.set({'content-type':'image/png'});
        res.send(thumb);
    });
});

app.get('/app/lib.js', function(req, res, next) {
    res.set({
        'content-type':'application/javascript',
        'cache-control':'max-age=3600'
    });
    res.send(tm.templates.libjs({
        cartoRef: require('carto').tree.Reference.data
    }));
});

app.get('/:newproject(new)', function(req, res, next) {
    res.redirect('/project?id=' + tm.tmpid());
});

app.get('/:history()', function(req, res, next) {
    res.set({'content-type':'text/html'});
    return res.send(tm.templates.history({
        history: req.history
    }));
});

app.use(function(err, req, res, next) {
    // Error on loading a tile, send 404.
    if (err && 'z' in req.params) return res.send(err.toString(), 404);
    // Otherwise 500 for now.
    if (/application\/json/.test(req.headers.accept)) {
        res.set({'content-type':'application/javascript'});
        res.send({message:err.toString()}, 500);
    } else if (/text\/html/.test(req.headers.accept)) {
        // @TODO error page template.
        res.send(err.toString(), 500);
    } else {
        res.send(err.toString(), 500);
    }
});

app.listen(3000);
console.log('TM2 @ http://localhost:3000/');
<commit_msg>Add XML export endpoint.<commit_after>var _ = require('underscore');
var qs = require('querystring');
var tm = require('./lib/tm');
var source = require('./lib/source');
var project = require('./lib/project');
var express = require('express');
var argv = require('optimist')
    .config('config')
    .argv;

var app = express();
app.use(express.bodyParser());
app.use(app.router);
app.use('/app', express.static(__dirname + '/app', { maxAge:3600e3 }));
app.use('/ext', express.static(__dirname + '/ext', { maxAge:3600e3 }));

app.param('project', function(req, res, next) {
    if (req.method === 'PUT' && req.body._recache && req.query.id) {
        source.invalidate(req.query.id, next);
    } else {
        next();
    }
}, function(req, res, next) {
    var id = req.query.id;
    var tmp = id && tm.tmpid(id);
    if (req.method === 'PUT') {
        var data = req.body;
    } else if (tmp && req.path === '/project') {
        var data = {
            styles: { 'style.mss': 'Map {\n  background-color:#fff;\n}\n\n#water {\n  polygon-fill:#b8dee6;\n}\n\n#road {\n  line-color:#ccc;\n}\n\n#admin {\n  line-color:rgba(0,32,64,0.2);\n}' },
            sources: ['mbstreets']
        };
    } else {
        var data = false;
    }
    project({
        id: id,
        data: data,
        perm: !tmp && !!data
    }, function(err, project) {
        if (err && !tmp) tm.history(id, true);
        if (err) return next(err);

        if (!tmp) tm.history(id);
        req.project = project;
        req.project.data._tmp = tmp;
        return next();
    });
});

app.param('history', function(req, res, next) {
    req.history = {};
    var history = tm.history().concat([]); // Clone.

    var load = function() {
        if (!history.length) return next();
        var id = history.shift();
        project.info(id, function(err, info) {
            if (err) {
                tm.history(id, true);
            } else {
                req.history[id] = info;
            }
            load();
        });
    };
    load();
});

app.put('/:project(project)', function(req, res, next) {
    res.send({
        _recache:false,
        mtime:req.project.data.mtime,
        background:req.project.data.background
    });
});

app.get('/:project(project)', function(req, res, next) {
    res.set({'content-type':'text/html'});
    return res.send(tm.templates.project({
        cartoRef: require('carto').tree.Reference.data,
        sources: [req.project._backend.data],
        library: _(source.library).filter(function(source, s) {
            return !_(req.project.data.sources).include(s);
        }),
        project: req.project.data
    }));
});

app.get('/:project(project)/:z/:x/:y.png', function(req, res, next) {
    var z = req.params.z | 0;
    var x = req.params.x | 0;
    var y = req.params.y | 0;
    req.project.getTile(z,x,y, function(err, data, headers) {
        if (err && err.message === 'Tilesource not loaded') {
            return res.redirect(req.path);
        } else if (err) {
            return next(err);
        }
        headers['cache-control'] = 'max-age=3600';
        res.set(headers);
        return res.send(data);
    });
});

app.get('/:project(project).xml', function(req, res, next) {
    res.set({'content-type':'text/xml'});
    return res.send(req.project._xml);
});

app.get('/thumb.png', function(req, res, next) {
    if (!req.query.id) return next(new Error('No id specified'));
    project.thumb({id:req.query.id}, function(err, thumb) {
        if (err) return next(err);
        res.set({'content-type':'image/png'});
        res.send(thumb);
    });
});

app.get('/app/lib.js', function(req, res, next) {
    res.set({
        'content-type':'application/javascript',
        'cache-control':'max-age=3600'
    });
    res.send(tm.templates.libjs({
        cartoRef: require('carto').tree.Reference.data
    }));
});

app.get('/:newproject(new)', function(req, res, next) {
    res.redirect('/project?id=' + tm.tmpid());
});

app.get('/:history()', function(req, res, next) {
    res.set({'content-type':'text/html'});
    return res.send(tm.templates.history({
        history: req.history
    }));
});

app.use(function(err, req, res, next) {
    // Error on loading a tile, send 404.
    if (err && 'z' in req.params) return res.send(err.toString(), 404);
    // Otherwise 500 for now.
    if (/application\/json/.test(req.headers.accept)) {
        res.set({'content-type':'application/javascript'});
        res.send({message:err.toString()}, 500);
    } else if (/text\/html/.test(req.headers.accept)) {
        // @TODO error page template.
        res.send(err.toString(), 500);
    } else {
        res.send(err.toString(), 500);
    }
});

app.listen(3000);
console.log('TM2 @ http://localhost:3000/');
<|endoftext|>


<commit_before>/* global Promise:false, define:false, module:false */

'use strict'

var shortid = require('shortid')
var defer = require('mini-defer')

// Proxy references for testing
var _self
var _MessageChannel

if (typeof define === 'function' && define.amd) {
  define('msgr', function () { return msgr })
} else if (typeof module === 'object' && module.exports) {
  module.exports = msgr
} else {
  self.msgr = msgr
}

/**
 * Initialise a client or worker to send and receive messages.
 * @param {Object} messageHandlers
 * @param {ServiceWorkerRegistration} worker
 * @returns {Channel}
 */
function msgr (messageHandlers, worker) {
  return new Channel(messageHandlers, worker)
}

/**
 * Initialise a worker to send and receive messages to and from the client.
 * @param {Object} messageHandlers
 * @returns {Channel}
 */
msgr.worker = function (messageHandlers) {
  return msgr(messageHandlers, null)
}

/**
 * Initialise a client to send and receive messages to and from the worker.
 * @param {ServiceWorkerRegistration} worker
 * @param {Object} messageHandlers
 * @returns {Channel}
 */
msgr.client = function (worker, messageHandlers, __mockMessageChannel) {
  _self = __mockMessageChannel ? {} : self
  _MessageChannel = __mockMessageChannel || self.MessageChannel
  return msgr(messageHandlers, worker)
}

// Reserved message types
msgr.types = {
  CONNECT: '@@MSGR/CONNECT',
  UNKNOWN: '@@MSGR/UNKNOWN',
  RESPONSE: '@@MSGR/RESPONSE'
}

// ---
// App
// ---

function Channel (handlers, worker) {
  this.handlers = handlers
  this.isClient = Boolean(worker)
  this.isWorker = !this.isClient

  // Is the comms channel open?
  this.open = defer()

  // Handlers for unknown message types
  this.receiveHandlers = []

  // ID/response handlers use for handling `responds()`s
  // key = id string, value = function
  this.responseHandlers = {}

  if (this.isClient) {
    this.open.resolve()
    this.recipient = worker
    this.messageChannel = new _MessageChannel()
    this.messageChannel.port1.onmessage = this._handleMessage.bind(this)
    this.send(msgr.types.CONNECT, 'connect')
  } else {
    _self.onmessage = this._handleMessage.bind(this)
  }
}

/**
 * Handle a message received from the client or worker.
 * @param {Object} event
 * @private
 */
Channel.prototype._handleMessage = function (event) {
  try {
    var request = JSON.parse(event.data)
    var id = request.id
  } catch (err) {
    throw new Error('msgr: ignoring malformed message')
  }

  var responder = function (data) {
    this.send(msgr.types.RESPONSE, data, id)
  }.bind(this)

  if (this.isWorker && request.type === msgr.types.CONNECT) {
    // Special init message type that gives us the port
    // that we will be sending messages to the client over
    this.recipient = event.ports[0]
    this.open.resolve()
  }

  if (request.type in this.handlers) {
    // Known message type, invoke registered handler
    this.handlers[request.type](request.data, responder)
  } else if (id && id in this.responseHandlers) {
    // Response to a message, invoke registered response handler
    var handler = this.responseHandlers[id]
    handler(request.data)
    this.responseHandlers[id] = null
  } else {
    // Unknown message type, invoke receive handlers
    this.receiveHandlers.forEach(function (handler) {
      handler(request.data, responder)
    })
  }
}

Channel.prototype.ready = function (fn) {
  this.open.promise.then(fn)
}

/**
 * Receive an "unknown" message that does not have a predefined handler.
 * @param {Function} handler
 */
Channel.prototype.receive = function (handler) {
  if (typeof handler !== 'function') {
    throw new Error('msgr: expecting handler to be a function')
  }
  this.receiveHandlers.push(handler)
}

/**
 * Send a message.
 * @param {String|*} type The message type or message data
 * @param {*} [data] The message data
 * @param {String} [_id] Message ID if this is a response
 * @returns {{then: then}}
 */
Channel.prototype.send = function (type, data, _id) {
  _id = _id || shortid.generate()

  var _this = this

  if (!data) {
    data = type
    if (type !== msgr.types.RESPONSE) {
      type = msgr.types.UNKNOWN
    }
  }

  var payload = JSON.stringify({
    type: type,
    data: data,
    id: _id
  })

  var args = [payload]

  if (this.isClient) {
    args.push([this.messageChannel.port2])
  }

  this.open.promise.then(function () {
    this.recipient.postMessage.apply(this.recipient, args)
  }.bind(this))

  return {
    /**
     * Register a one-off response handler for a message.
     * @param {Function} handler
     */
    then: function (handler) {
      if (_this.responseHandlers[_id]) {
        throw new Error('msgr: you can register only one response handler')
      }
      _this.responseHandlers[_id] = handler
    }
  }
}
<commit_msg>Fix linting errors<commit_after>/* global Promise:false, define:false, module:false, self:false */

'use strict'

var shortid = require('shortid')
var defer = require('mini-defer')

// Proxy references for testing
var _self
var _MessageChannel

if (typeof define === 'function' && define.amd) {
  define('msgr', function () { return msgr })
} else if (typeof module === 'object' && module.exports) {
  module.exports = msgr
} else {
  self.msgr = msgr
}

/**
 * Initialise a client or worker to send and receive messages.
 * @param {Object} messageHandlers
 * @param {ServiceWorkerRegistration} worker
 * @returns {Channel}
 */
function msgr (messageHandlers, worker) {
  return new Channel(messageHandlers, worker)
}

/**
 * Initialise a worker to send and receive messages to and from the client.
 * @param {Object} messageHandlers
 * @returns {Channel}
 */
msgr.worker = function (messageHandlers) {
  return msgr(messageHandlers, null)
}

/**
 * Initialise a client to send and receive messages to and from the worker.
 * @param {ServiceWorkerRegistration} worker
 * @param {Object} messageHandlers
 * @returns {Channel}
 */
msgr.client = function (worker, messageHandlers, __mockMessageChannel) {
  _self = __mockMessageChannel ? {} : self
  _MessageChannel = __mockMessageChannel || self.MessageChannel
  return msgr(messageHandlers, worker)
}

// Reserved message types
msgr.types = {
  CONNECT: '@@MSGR/CONNECT',
  UNKNOWN: '@@MSGR/UNKNOWN',
  RESPONSE: '@@MSGR/RESPONSE'
}

// ---
// App
// ---

function Channel (handlers, worker) {
  this.handlers = handlers
  this.isClient = Boolean(worker)
  this.isWorker = !this.isClient

  // Is the comms channel open?
  this.open = defer()

  // Handlers for unknown message types
  this.receiveHandlers = []

  // ID/response handlers use for handling `responds()`s
  // key = id string, value = function
  this.responseHandlers = {}

  if (this.isClient) {
    this.open.resolve()
    this.recipient = worker
    this.messageChannel = new _MessageChannel()
    this.messageChannel.port1.onmessage = this._handleMessage.bind(this)
    this.send(msgr.types.CONNECT, 'connect')
  } else {
    _self.onmessage = this._handleMessage.bind(this)
  }
}

/**
 * Handle a message received from the client or worker.
 * @param {Object} event
 * @private
 */
Channel.prototype._handleMessage = function (event) {
  try {
    var request = JSON.parse(event.data)
    var id = request.id
  } catch (err) {
    throw new Error('msgr: ignoring malformed message')
  }

  var responder = function (data) {
    this.send(msgr.types.RESPONSE, data, id)
  }.bind(this)

  if (this.isWorker && request.type === msgr.types.CONNECT) {
    // Special init message type that gives us the port
    // that we will be sending messages to the client over
    this.recipient = event.ports[0]
    this.open.resolve()
  }

  if (request.type in this.handlers) {
    // Known message type, invoke registered handler
    this.handlers[request.type](request.data, responder)
  } else if (id && id in this.responseHandlers) {
    // Response to a message, invoke registered response handler
    var handler = this.responseHandlers[id]
    handler(request.data)
    this.responseHandlers[id] = null
  } else {
    // Unknown message type, invoke receive handlers
    this.receiveHandlers.forEach(function (handler) {
      handler(request.data, responder)
    })
  }
}

Channel.prototype.ready = function (fn) {
  this.open.promise.then(fn)
}

/**
 * Receive an "unknown" message that does not have a predefined handler.
 * @param {Function} handler
 */
Channel.prototype.receive = function (handler) {
  if (typeof handler !== 'function') {
    throw new Error('msgr: expecting handler to be a function')
  }
  this.receiveHandlers.push(handler)
}

/**
 * Send a message.
 * @param {String|*} type The message type or message data
 * @param {*} [data] The message data
 * @param {String} [_id] Message ID if this is a response
 * @returns {{then: then}}
 */
Channel.prototype.send = function (type, data, _id) {
  _id = _id || shortid.generate()

  var _this = this

  if (!data) {
    data = type
    if (type !== msgr.types.RESPONSE) {
      type = msgr.types.UNKNOWN
    }
  }

  var payload = JSON.stringify({
    type: type,
    data: data,
    id: _id
  })

  var args = [payload]

  if (this.isClient) {
    args.push([this.messageChannel.port2])
  }

  this.open.promise.then(function () {
    this.recipient.postMessage.apply(this.recipient, args)
  }.bind(this))

  return {
    /**
     * Register a one-off response handler for a message.
     * @param {Function} handler
     */
    then: function (handler) {
      if (_this.responseHandlers[_id]) {
        throw new Error('msgr: you can register only one response handler')
      }
      _this.responseHandlers[_id] = handler
    }
  }
}
<|endoftext|>


<commit_before>require('coffee-script/register');
require('dotenv').load({silent: true});

var tweets = require('./lib/tweets');

module.exports = {
  search       : tweets.search,
  stream       : tweets.stream,
  DEFAULT_QUERY: '#RiodosSinos from:ComusaNH'
};
<commit_msg>Update default query<commit_after>require('coffee-script/register');
require('dotenv').load({silent: true});

var tweets = require('./lib/tweets');

module.exports = {
  search       : tweets.search,
  stream       : tweets.stream,
  DEFAULT_QUERY: '#RiodosSinos #NovoHamburgo from:ComusaNH'
};
<|endoftext|>


<commit_before>var should = require("should"),
    requirejs = require('requirejs');

requirejs.config({
    baseUrl: 'pulsr',
    nodeRequire: require
});

describe('Pulsr', function (){
    describe('fileHandler', function (){
        it('should return 200 statusCode for image file request within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'fs', 'path', 'module', 'http', 'conf'], function (async, fs, path, module, http, conf) {
                var imgDirs = ['img'];

                function traverseDir(dirName, done) {
                    var dirPath = path.join(path.dirname(path.resolve(module.uri)), '../', dirName);

                    function requestFile(fileName, done) {
                        if (/.+\.(png|jpg|gif)/.test(fileName)) {
                            var filePath = path.join(dirPath, fileName);

                            fs.stat(filePath, function (err, stat) {
                                if (err) {
                                    throw err;
                                }
                                else if (stat.isFile()){
                                    http.get('http://' + conf.app.domains.static + '/' + dirName + '/' + fileName, function (response) {
                                        response.should.have.status(200);
                                        done();
                                    });
                                }
                                else{
                                    done();
                                }
                            });
                        }
                        else{
                            done();
                        }
                    }

                    fs.readdir(dirPath, function (err, files) {
                        if (err) {
                            throw err;
                        }
                        else{
                            async.forEach(files, requestFile, function (err) {
                                done();
                            });
                        }
                    });
                }

                async.forEach(imgDirs, traverseDir, function (err) {
                    done();
                });
            });
        });

        it('should return 404 statusCode for non-existing image file requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var imgFiles = ['img/apple.png', 'img/banana.png', 'img/jake.gif', 'img/zoo.jpg'];

                function requestFile(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(404);
                        done();
                    });
                }

                async.forEach(imgFiles, requestFile, function (err) {
                    done();
                });
            });
        });

        it('should return 403 statusCode for direct directory access requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var restrictedDirs = ['img', 'js', 'less', 'pulsr', 'controller', 'nonExistingDir', 'js/bootstrap', 'less/foundation'];

                function requestDir(dirName, done) {
                    http.get('http://' + conf.app.domains.static + '/' + dirName, function (response) {
                        response.should.have.status(403);
                        done();
                    });
                }

                async.forEach(restrictedDirs, requestDir, function (err) {
                    done();
                });
            });
        });

        it('should return 403 statusCode for restricted directory access requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var restrictedDirs = ['pulsr/conf.js', 'pagelets/ga/ga.js', 'contents/404.md', 'controllers/api.js', 'node_modules/bin/docco', 'nonExistingDir', 'tests/baseController.js', 'views/layout.hb', '../aboveRootNonExistDir'];

                function requestDir(dirName, done) {
                    http.get('http://' + conf.app.domains.static + '/' + dirName, function (response) {
                        response.should.have.status(403);
                        done();
                    });
                }

                async.forEach(restrictedDirs, requestDir, function (err) {
                    done();
                });
            });
        });
    });
});<commit_msg>Test: added test cases to fileHandler to request favicon icons.<commit_after>var should = require("should"),
    requirejs = require('requirejs');

requirejs.config({
    baseUrl: 'pulsr',
    nodeRequire: require
});

describe('Pulsr', function (){
    describe('fileHandler', function (){
        it('should return 200 statusCode for image file request within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'fs', 'path', 'module', 'http', 'conf'], function (async, fs, path, module, http, conf) {
                var imgDirs = ['img'];

                function traverseDir(dirName, done) {
                    var dirPath = path.join(path.dirname(path.resolve(module.uri)), '../', dirName);

                    function requestFile(fileName, done) {
                        if (/.+\.(png|jpg|gif)/.test(fileName)) {
                            var filePath = path.join(dirPath, fileName);

                            fs.stat(filePath, function (err, stat) {
                                if (err) {
                                    throw err;
                                }
                                else if (stat.isFile()){
                                    http.get('http://' + conf.app.domains.static + '/' + dirName + '/' + fileName, function (response) {
                                        response.should.have.status(200);
                                        done();
                                    });
                                }
                                else{
                                    done();
                                }
                            });
                        }
                        else{
                            done();
                        }
                    }

                    fs.readdir(dirPath, function (err, files) {
                        if (err) {
                            throw err;
                        }
                        else{
                            async.forEach(files, requestFile, function (err) {
                                done();
                            });
                        }
                    });
                }

                async.forEach(imgDirs, traverseDir, function (err) {
                    done();
                });
            });
        });

        it('should return 404 statusCode for non-existing image file requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var imgFiles = ['img/apple.png', 'img/banana.png', 'img/jake.gif', 'img/zoo.jpg'];

                function requestFile(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(404);
                        done();
                    });
                }

                async.forEach(imgFiles, requestFile, function (err) {
                    done();
                });
            });
        });

        it('should return 403 statusCode for direct directory access requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var restrictedDirs = ['img', 'js', 'less', 'pulsr', 'controller', 'nonExistingDir', 'js/bootstrap', 'less/foundation', 'assets'];

                function requestDir(dirName, done) {
                    http.get('http://' + conf.app.domains.static + '/' + dirName, function (response) {
                        response.should.have.status(403);
                        done();
                    });
                }

                async.forEach(restrictedDirs, requestDir, function (err) {
                    done();
                });
            });
        });

        it('should return 403 statusCode for restricted directory access requests within 20s', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var restrictedDirs = ['pulsr/conf.js', 'pagelets/ga/ga.js', 'contents/404.md', 'controllers/api.js', 'node_modules/bin/docco', 'nonExistingDir', 'tests/baseController.js', 'views/layout.hb', '../aboveRootNonExistDir', 'app.js', '.travis.yml', '.gitignore', 'Makefile', 'package.json', 'assets'];

                function requestDir(dirName, done) {
                    http.get('http://' + conf.app.domains.static + '/' + dirName, function (response) {
                        response.should.have.status(403);
                        done();
                    });
                }

                async.forEach(restrictedDirs, requestDir, function (err) {
                    done();
                });
            });
        });

        it('should return 200 statusCode for favicon requests located in the root directory.', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var favicons = ['favicon.ico', 'apple-touch-icon-57x57-precomposed.png'];

                function sendRequest(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(200);
                        done();
                    });
                }

                async.forEach(favicons, sendRequest, function (err) {
                    console.log('favicon is done');
                    done();
                });
            });
        });

        it('should return 200 statusCode for plain text requests located in the root directory.', function (done){
            // set timeout to 20s
            this.timeout(20000);

            requirejs(['async', 'http', 'conf'], function (async, http, conf) {
                var favicons = ['robots.txt', 'humans.txt', 'README.md', 'CHANGELOG.md'];

                function sendRequest(filePath, done) {
                    http.get('http://' + conf.app.domains.static + '/' + filePath, function (response) {
                        response.should.have.status(200);
                        done();
                    });
                }

                async.forEach(favicons, sendRequest, function (err) {
                    console.log('favicon is done');
                    done();
                });
            });
        });
    });
});<|endoftext|>


<commit_before>var debug = require("debug");
var debugLog = debug("btcexp:config");

var fs = require('fs');
var crypto = require('crypto');
var url = require('url');

var baseUrl = process.env.BTCEXP_BASEURL || "/";

var coins = require("./coins.js");
var credentials = require("./credentials.js");

var currentCoin = process.env.BTCEXP_COIN || "BTC";

var rpcCred = credentials.rpc;

if (rpcCred.cookie && !rpcCred.username && !rpcCred.password && fs.existsSync(rpcCred.cookie)) {
	console.log(`Loading RPC cookie file: ${rpcCred.cookie}`);

	[ rpcCred.username, rpcCred.password ] = fs.readFileSync(rpcCred.cookie).toString().split(':', 2);

	if (!rpcCred.password) {
		throw new Error(`Cookie file ${rpcCred.cookie} in unexpected format`);
	}
}

var cookieSecret = process.env.BTCEXP_COOKIE_SECRET
 || (rpcCred.password && crypto.createHmac('sha256', JSON.stringify(rpcCred))
                               .update('btc-rpc-explorer-cookie-secret').digest('hex'))
 || "0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";

var electrumXServerUriStrings = (process.env.BTCEXP_ELECTRUMX_SERVERS || "").split(',').filter(Boolean);
var electrumXServers = [];
for (var i = 0; i < electrumXServerUriStrings.length; i++) {
	var uri = url.parse(electrumXServerUriStrings[i]);

	electrumXServers.push({protocol:uri.protocol.substring(0, uri.protocol.length - 1), host:uri.hostname, port:parseInt(uri.port)});
}

// default=false env vars
[
	"BTCEXP_DEMO",
	"BTCEXP_PRIVACY_MODE",
	"BTCEXP_NO_INMEMORY_RPC_CACHE",
	"BTCEXP_RPC_ALLOWALL"

].forEach(function(item) {
	if (process.env[item] === undefined) {
		process.env[item] = "false";

		debugLog(`Config(default): ${item}=false`)
	}
});

// default=true env vars
[
	"BTCEXP_NO_RATES",
	"BTCEXP_UI_SHOW_TOOLS_SUBHEADER",
	"BTCEXP_SLOW_DEVICE_MODE"

].forEach(function(item) {
	if (process.env[item] === undefined) {
		process.env[item] = "true";

		debugLog(`Config(default): ${item}=true`)
	}
});

module.exports = {
	baseUrl: baseUrl,

        coin: currentCoin,

	cookieSecret: cookieSecret,

	privacyMode: (process.env.BTCEXP_PRIVACY_MODE.toLowerCase() == "true"),
	slowDeviceMode: (process.env.BTCEXP_SLOW_DEVICE_MODE.toLowerCase() == "true"),
	demoSite: (process.env.BTCEXP_DEMO.toLowerCase() == "true"),
	queryExchangeRates: (process.env.BTCEXP_NO_RATES.toLowerCase() != "true"),
	noInmemoryRpcCache: (process.env.BTCEXP_NO_INMEMORY_RPC_CACHE.toLowerCase() == "true"),

	rpcConcurrency: (process.env.BTCEXP_RPC_CONCURRENCY || 10),

	rpcBlacklist:
	  process.env.BTCEXP_RPC_ALLOWALL.toLowerCase() == "true"  ? []
	: process.env.BTCEXP_RPC_BLACKLIST ? process.env.BTCEXP_RPC_BLACKLIST.split(',').filter(Boolean)
	: [
		"addnode",
		"backupwallet",
		"bumpfee",
		"clearbanned",
		"createmultisig",
		"createwallet",
		"disconnectnode",
		"dumpprivkey",
		"dumpwallet",
		"encryptwallet",
		"generate",
		"generatetoaddress",
		"getaccountaddrss",
		"getaddressesbyaccount",
		"getbalance",
		"getnewaddress",
		"getrawchangeaddress",
		"getreceivedbyaccount",
		"getreceivedbyaddress",
		"gettransaction",
		"getunconfirmedbalance",
		"getwalletinfo",
		"importaddress",
		"importmulti",
		"importprivkey",
		"importprunedfunds",
		"importpubkey",
		"importwallet",
		"invalidateblock",
		"keypoolrefill",
		"listaccounts",
		"listaddressgroupings",
		"listlockunspent",
		"listreceivedbyaccount",
		"listreceivedbyaddress",
		"listsinceblock",
		"listtransactions",
		"listunspent",
		"listwallets",
		"lockunspent",
		"logging",
		"move",
		"preciousblock",
		"pruneblockchain",
		"reconsiderblock",
		"removeprunedfunds",
		"rescanblockchain",
		"savemempool",
		"sendfrom",
		"sendmany",
		"sendtoaddress",
		"setaccount",
		"setban",
		"setmocktime",
		"setnetworkactive",
		"signmessage",
		"signmessagewithprivatekey",
		"signrawtransaction",
		"signrawtransactionwithkey",
		"stop",
		"submitblock",
		"syncwithvalidationinterfacequeue",
		"verifychain",
		"waitforblock",
		"waitforblockheight",
		"waitfornewblock",
		"walletlock",
		"walletpassphrase",
		"walletpassphrasechange",
	],

	addressApi:process.env.BTCEXP_ADDRESS_API,
	electrumXServers:electrumXServers,

	redisUrl:process.env.BTCEXP_REDIS_URL,

	site: {
		homepage:{
			recentBlocksCount:10
		},
		blockTxPageSize:20,
		addressTxPageSize:10,
		txMaxInput:15,
		browseBlocksPageSize:50,
		addressPage:{
			txOutputMaxDefaultDisplay:10
		},
		valueDisplayMaxLargeDigits: 4,
		header:{
			showToolsSubheader:(process.env.BTCEXP_UI_SHOW_TOOLS_SUBHEADER == "true"),
			dropdowns:[
				{
					title:"Related Sites",
					links:[
						{name: "Bitcoin Explorer", url:"https://explorer.btc21.org", imgUrl:"./img/logo/btc.svg"},
						{name: "Testnet Explorer", url:"https://testnet.btc21.org", imgUrl:"./img/logo/tbtc.svg"},
						{name: "LND Admin", url:"https://lnd-admin.btc21.org", imgUrl:"./img/logo/lnd-admin.png"},
						//{name: "Litecoin Explorer", url:"https://ltc.chaintools.io", imgUrl:"/img/logo/ltc.svg"},
						//{name: "Lightning Explorer", url:"https://lightning.chaintools.io", imgUrl:"/img/logo/lightning.svg"},
					]
				}
			]
		},
		subHeaderToolsList:[0, 10, 9, 4, 11, 6, 7], // indexes in "siteTools" below that are shown in the site "sub menu" (visible on all pages except homepage)
		prioritizedToolIdsList: [0, 10, 11, 9, 3, 4, 12, 2, 5, 1, 6, 7, 8],
	},

	credentials: credentials,

	siteTools:[
	/* 0 */		{name:"Node Status", url:"./node-status", desc:"Summary of this node: version, network, uptime, etc.", fontawesome:"fas fa-broadcast-tower"},
	/* 1 */		{name:"Peers", url:"./peers", desc:"Detailed info about the peers connected to this node.", fontawesome:"fas fa-sitemap"},

	/* 2 */		{name:"Browse Blocks", url:"./blocks", desc:"Browse all blocks in the blockchain.", fontawesome:"fas fa-cubes"},
	/* 3 */		{name:"Transaction Stats", url:"./tx-stats", desc:"See graphs of total transaction volume and transaction rates.", fontawesome:"fas fa-chart-bar"},

	/* 4 */		{name:"Mempool Summary", url:"./mempool-summary", desc:"Detailed summary of the current mempool for this node.", fontawesome:"fas fa-receipt"},
	/* 5 */		{name:"Browse Pending Tx", url:"./unconfirmed-tx", desc:"Browse unconfirmed/pending transactions.", fontawesome:"fas fa-unlock"},

	/* 6 */		{name:"RPC Browser", url:"./rpc-browser", desc:"Browse the RPC functionality of this node. See docs and execute commands.", fontawesome:"fas fa-book"},
	/* 7 */		{name:"RPC Terminal", url:"./rpc-terminal", desc:"Directly execute RPCs against this node.", fontawesome:"fas fa-terminal"},

	/* 8 */		{name:(coins[currentCoin].name + " Fun"), url:"./fun", desc:"See fun/interesting historical blockchain data.", fontawesome:"fas fa-certificate"},

	/* 9 */		{name:"Mining Summary", url:"./mining-summary", desc:"Summary of recent data about miners.", fontawesome:"fas fa-chart-pie"},
	/* 10 */	{name:"Block Stats", url:"./block-stats", desc:"Summary data for blocks in configurable range.", fontawesome:"fas fa-layer-group"},
	/* 11 */	{name:"Block Analysis", url:"./block-analysis", desc:"Summary analysis for all transactions in a block.", fontawesome:"fas fa-angle-double-down"},
	/* 12 */	{name:"Difficulty History", url:"./difficulty-history", desc:"Graph of difficulty changes over time.", fontawesome:"fas fa-chart-line"},
	],

	donations:{
		addresses:{
			coins:["BTC"],
			sites:{"BTC":"https://explorer.btc21.org"}
		},
		btcpayserver:{
			host:"https://donate.btc21.org"
		}
	}
};

debugLog(`Config(final): privacyMode=${module.exports.privacyMode}`);
debugLog(`Config(final): slowDeviceMode=${module.exports.slowDeviceMode}`);
debugLog(`Config(final): demo=${module.exports.demoSite}`);
debugLog(`Config(final): rpcAllowAll=${module.exports.rpcBlacklist.length == 0}`);
<commit_msg>Append slash if needed<commit_after>var debug = require("debug");
var debugLog = debug("btcexp:config");

var fs = require('fs');
var crypto = require('crypto');
var url = require('url');

var baseUrl = process.env.BTCEXP_BASEURL || "/";
if(!baseUrl.endsWith('/')) {
	baseUrl += '/';
}

var coins = require("./coins.js");
var credentials = require("./credentials.js");

var currentCoin = process.env.BTCEXP_COIN || "BTC";

var rpcCred = credentials.rpc;

if (rpcCred.cookie && !rpcCred.username && !rpcCred.password && fs.existsSync(rpcCred.cookie)) {
	console.log(`Loading RPC cookie file: ${rpcCred.cookie}`);

	[ rpcCred.username, rpcCred.password ] = fs.readFileSync(rpcCred.cookie).toString().split(':', 2);

	if (!rpcCred.password) {
		throw new Error(`Cookie file ${rpcCred.cookie} in unexpected format`);
	}
}

var cookieSecret = process.env.BTCEXP_COOKIE_SECRET
 || (rpcCred.password && crypto.createHmac('sha256', JSON.stringify(rpcCred))
                               .update('btc-rpc-explorer-cookie-secret').digest('hex'))
 || "0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";

var electrumXServerUriStrings = (process.env.BTCEXP_ELECTRUMX_SERVERS || "").split(',').filter(Boolean);
var electrumXServers = [];
for (var i = 0; i < electrumXServerUriStrings.length; i++) {
	var uri = url.parse(electrumXServerUriStrings[i]);

	electrumXServers.push({protocol:uri.protocol.substring(0, uri.protocol.length - 1), host:uri.hostname, port:parseInt(uri.port)});
}

// default=false env vars
[
	"BTCEXP_DEMO",
	"BTCEXP_PRIVACY_MODE",
	"BTCEXP_NO_INMEMORY_RPC_CACHE",
	"BTCEXP_RPC_ALLOWALL"

].forEach(function(item) {
	if (process.env[item] === undefined) {
		process.env[item] = "false";

		debugLog(`Config(default): ${item}=false`)
	}
});

// default=true env vars
[
	"BTCEXP_NO_RATES",
	"BTCEXP_UI_SHOW_TOOLS_SUBHEADER",
	"BTCEXP_SLOW_DEVICE_MODE"

].forEach(function(item) {
	if (process.env[item] === undefined) {
		process.env[item] = "true";

		debugLog(`Config(default): ${item}=true`)
	}
});

module.exports = {
	baseUrl: baseUrl,

        coin: currentCoin,

	cookieSecret: cookieSecret,

	privacyMode: (process.env.BTCEXP_PRIVACY_MODE.toLowerCase() == "true"),
	slowDeviceMode: (process.env.BTCEXP_SLOW_DEVICE_MODE.toLowerCase() == "true"),
	demoSite: (process.env.BTCEXP_DEMO.toLowerCase() == "true"),
	queryExchangeRates: (process.env.BTCEXP_NO_RATES.toLowerCase() != "true"),
	noInmemoryRpcCache: (process.env.BTCEXP_NO_INMEMORY_RPC_CACHE.toLowerCase() == "true"),

	rpcConcurrency: (process.env.BTCEXP_RPC_CONCURRENCY || 10),

	rpcBlacklist:
	  process.env.BTCEXP_RPC_ALLOWALL.toLowerCase() == "true"  ? []
	: process.env.BTCEXP_RPC_BLACKLIST ? process.env.BTCEXP_RPC_BLACKLIST.split(',').filter(Boolean)
	: [
		"addnode",
		"backupwallet",
		"bumpfee",
		"clearbanned",
		"createmultisig",
		"createwallet",
		"disconnectnode",
		"dumpprivkey",
		"dumpwallet",
		"encryptwallet",
		"generate",
		"generatetoaddress",
		"getaccountaddrss",
		"getaddressesbyaccount",
		"getbalance",
		"getnewaddress",
		"getrawchangeaddress",
		"getreceivedbyaccount",
		"getreceivedbyaddress",
		"gettransaction",
		"getunconfirmedbalance",
		"getwalletinfo",
		"importaddress",
		"importmulti",
		"importprivkey",
		"importprunedfunds",
		"importpubkey",
		"importwallet",
		"invalidateblock",
		"keypoolrefill",
		"listaccounts",
		"listaddressgroupings",
		"listlockunspent",
		"listreceivedbyaccount",
		"listreceivedbyaddress",
		"listsinceblock",
		"listtransactions",
		"listunspent",
		"listwallets",
		"lockunspent",
		"logging",
		"move",
		"preciousblock",
		"pruneblockchain",
		"reconsiderblock",
		"removeprunedfunds",
		"rescanblockchain",
		"savemempool",
		"sendfrom",
		"sendmany",
		"sendtoaddress",
		"setaccount",
		"setban",
		"setmocktime",
		"setnetworkactive",
		"signmessage",
		"signmessagewithprivatekey",
		"signrawtransaction",
		"signrawtransactionwithkey",
		"stop",
		"submitblock",
		"syncwithvalidationinterfacequeue",
		"verifychain",
		"waitforblock",
		"waitforblockheight",
		"waitfornewblock",
		"walletlock",
		"walletpassphrase",
		"walletpassphrasechange",
	],

	addressApi:process.env.BTCEXP_ADDRESS_API,
	electrumXServers:electrumXServers,

	redisUrl:process.env.BTCEXP_REDIS_URL,

	site: {
		homepage:{
			recentBlocksCount:10
		},
		blockTxPageSize:20,
		addressTxPageSize:10,
		txMaxInput:15,
		browseBlocksPageSize:50,
		addressPage:{
			txOutputMaxDefaultDisplay:10
		},
		valueDisplayMaxLargeDigits: 4,
		header:{
			showToolsSubheader:(process.env.BTCEXP_UI_SHOW_TOOLS_SUBHEADER == "true"),
			dropdowns:[
				{
					title:"Related Sites",
					links:[
						{name: "Bitcoin Explorer", url:"https://explorer.btc21.org", imgUrl:"./img/logo/btc.svg"},
						{name: "Testnet Explorer", url:"https://testnet.btc21.org", imgUrl:"./img/logo/tbtc.svg"},
						{name: "LND Admin", url:"https://lnd-admin.btc21.org", imgUrl:"./img/logo/lnd-admin.png"},
						//{name: "Litecoin Explorer", url:"https://ltc.chaintools.io", imgUrl:"/img/logo/ltc.svg"},
						//{name: "Lightning Explorer", url:"https://lightning.chaintools.io", imgUrl:"/img/logo/lightning.svg"},
					]
				}
			]
		},
		subHeaderToolsList:[0, 10, 9, 4, 11, 6, 7], // indexes in "siteTools" below that are shown in the site "sub menu" (visible on all pages except homepage)
		prioritizedToolIdsList: [0, 10, 11, 9, 3, 4, 12, 2, 5, 1, 6, 7, 8],
	},

	credentials: credentials,

	siteTools:[
	/* 0 */		{name:"Node Status", url:"./node-status", desc:"Summary of this node: version, network, uptime, etc.", fontawesome:"fas fa-broadcast-tower"},
	/* 1 */		{name:"Peers", url:"./peers", desc:"Detailed info about the peers connected to this node.", fontawesome:"fas fa-sitemap"},

	/* 2 */		{name:"Browse Blocks", url:"./blocks", desc:"Browse all blocks in the blockchain.", fontawesome:"fas fa-cubes"},
	/* 3 */		{name:"Transaction Stats", url:"./tx-stats", desc:"See graphs of total transaction volume and transaction rates.", fontawesome:"fas fa-chart-bar"},

	/* 4 */		{name:"Mempool Summary", url:"./mempool-summary", desc:"Detailed summary of the current mempool for this node.", fontawesome:"fas fa-receipt"},
	/* 5 */		{name:"Browse Pending Tx", url:"./unconfirmed-tx", desc:"Browse unconfirmed/pending transactions.", fontawesome:"fas fa-unlock"},

	/* 6 */		{name:"RPC Browser", url:"./rpc-browser", desc:"Browse the RPC functionality of this node. See docs and execute commands.", fontawesome:"fas fa-book"},
	/* 7 */		{name:"RPC Terminal", url:"./rpc-terminal", desc:"Directly execute RPCs against this node.", fontawesome:"fas fa-terminal"},

	/* 8 */		{name:(coins[currentCoin].name + " Fun"), url:"./fun", desc:"See fun/interesting historical blockchain data.", fontawesome:"fas fa-certificate"},

	/* 9 */		{name:"Mining Summary", url:"./mining-summary", desc:"Summary of recent data about miners.", fontawesome:"fas fa-chart-pie"},
	/* 10 */	{name:"Block Stats", url:"./block-stats", desc:"Summary data for blocks in configurable range.", fontawesome:"fas fa-layer-group"},
	/* 11 */	{name:"Block Analysis", url:"./block-analysis", desc:"Summary analysis for all transactions in a block.", fontawesome:"fas fa-angle-double-down"},
	/* 12 */	{name:"Difficulty History", url:"./difficulty-history", desc:"Graph of difficulty changes over time.", fontawesome:"fas fa-chart-line"},
	],

	donations:{
		addresses:{
			coins:["BTC"],
			sites:{"BTC":"https://explorer.btc21.org"}
		},
		btcpayserver:{
			host:"https://donate.btc21.org"
		}
	}
};

debugLog(`Config(final): privacyMode=${module.exports.privacyMode}`);
debugLog(`Config(final): slowDeviceMode=${module.exports.slowDeviceMode}`);
debugLog(`Config(final): demo=${module.exports.demoSite}`);
debugLog(`Config(final): rpcAllowAll=${module.exports.rpcBlacklist.length == 0}`);
<|endoftext|>


<commit_before>jQuery(function() {

					module("hand");
					test(
							"a basic hand visibility test",
							function() {
								ok((jQuery('span#handGallery').length == 1),
										"a span with id handGallery should be present and be unique");
								ok((jQuery('span#gallery').length == 1),
										"a span with id gallery should be present and be unique");
								var myGalleryContent = jQuery('span#gallery div');
								ok((myGalleryContent.length > 0),
										"it should itself contain other elements, actually found: "
												+ myGalleryContent.length);
								var slideElements = jQuery('span#gallery a.cross-link');
								ok((slideElements.length == 7),
										"these elements should contain the 7 images in the hand, actually found: "
												+ slideElements.length);
								var attribute = jQuery(
										'span#gallery a.cross-link:first img')
										.attr('src');
								ok(
										((attribute.indexOf("cards") != -1) && (attribute
												.indexOf(".jpg") != -1)),
										"the first of these images should have a source URL containing 'cards' and '.jpg', and it appears to be: "
												+ attribute);
							});
					module("menubar");
					test(
							"a basic menubar visibility test",
							function() {
								var menubaritem1 = jQuery('ul.jMenu a.fNiv img:first');
								ok(
										("/image/logoh1.gif" === menubaritem1
												.attr('src')),
										"the first menubar option is the first image of a <a> class fNiv contained in a <ul> of class jMenu, it should exist and point to '/image/logoh1.gif', and it appears to be: "
												+ menubaritem1.attr('src'));
								menubaritem1 = jQuery('ul.jMenu li ul li a:first');
								ok(
										(menubaritem1.text() === "About HatchetHarry"),
										"the very first menubar entry is the <a> contained in a <li>, a <ul>, a <li> in a <ul> of class jMenu"
												+ ", it should exist and contain 'About HatchetHarry', and it appears to be: "
												+ menubaritem1.text());

								var menubaritem2 = jQuery('ul.jMenu li.fNiv a:first');
								ok(
										(menubaritem2.text() === "Game"),
										"the second menubar option is the first <a> contained in a <li> class fNiv in a <ul> class jMenu."
												+ " It should exist and point to 'Game', and it appears to be: "
												+ menubaritem2.text());
							});
});<commit_msg>added a clock test with QUnit's asyncTest<commit_after>var before, after;

jQuery(function() {

					module("hand");
					test(
							"a basic hand visibility test",
							function() {
								ok((jQuery('span#handGallery').length == 1),
										"a span with id handGallery should be present and be unique");
								ok((jQuery('span#gallery').length == 1),
										"a span with id gallery should be present and be unique");
								var myGalleryContent = jQuery('span#gallery div');
								ok((myGalleryContent.length > 0),
										"it should itself contain other elements, actually found: "
												+ myGalleryContent.length);
								var slideElements = jQuery('span#gallery a.cross-link');
								ok((slideElements.length == 7),
										"these elements should contain the 7 images in the hand, actually found: "
												+ slideElements.length);
								var attribute = jQuery(
										'span#gallery a.cross-link:first img')
										.attr('src');
								ok(
										((attribute.indexOf("cards") != -1) && (attribute
												.indexOf(".jpg") != -1)),
										"the first of these images should have a source URL containing 'cards' and '.jpg', and it appears to be: "
												+ attribute);
							});
					module("menubar");
					test(
							"a basic menubar visibility test",
							function() {
								var menubaritem1 = jQuery('ul.jMenu a.fNiv img:first');
								ok(
										("/image/logoh1.gif" === menubaritem1
												.attr('src')),
										"the first menubar option is the first image of a <a> class fNiv contained in a <ul> of class jMenu, it should exist and point to '/image/logoh1.gif', and it appears to be: "
												+ menubaritem1.attr('src'));
								menubaritem1 = jQuery('ul.jMenu li ul li a:first');
								ok(
										(menubaritem1.text() === "About HatchetHarry"),
										"the very first menubar entry is the <a> contained in a <li>, a <ul>, a <li> in a <ul> of class jMenu"
												+ ", it should exist and contain 'About HatchetHarry', and it appears to be: "
												+ menubaritem1.text());

								var menubaritem2 = jQuery('ul.jMenu li.fNiv a:first');
								ok(
										(menubaritem2.text() === "Game"),
										"the second menubar option is the first <a> contained in a <li> class fNiv in a <ul> class jMenu."
												+ " It should exist and point to 'Game', and it appears to be: "
												+ menubaritem2.text());
							});
					module("clock");
					asyncTest(
							"test the clock",
							function() {
								expect(1);
								before = jQuery("#clock").text();

								window.setTimeout(function() {
									after = jQuery("#clock").text();
									ok((before !== after), "the clock should tick");
									start();
								}, 4000);
							});
});<|endoftext|>


<commit_before>/**
 * Created by Dominic Böttger on 28.05.2014
 * INSPIRATIONlabs GmbH
 * http://www.inspirationlabs.com
 */
var _ = require('underscore');
var fs = require('fs');
var collquery = require ('./lib/query');
var debug = require('debug')('mlcl_collections');

var molecuel;

/**
 * This module serves the molecuel elements the type definition for database objects
 * @todo implement the dynamic generation of elements
 * @constructor
 */
var collections = function () {
  var self = this;

  this.collectionRegistry = {};
  // emit molecuel elements pre init event
  molecuel.emit('mlcl::collections::init:pre', self);

  /**
   * Schema directory config
   */
  if (molecuel.config.collections && molecuel.config.collections.collectionDir) {
    this.collectionDir = molecuel.config.collections.collectionDir;
  }

  /**
   * Execute after successful elasticsearch connection
   */
  molecuel.on('mlcl::elements::init:post', function (elements) {
    // add the access to all the db's and searchfunctionality
    self.elements = elements;
    self.elastic = elements.elastic;
    self.database = elements.database;
    self.registerCollections();
    molecuel.emit('mlcl::collections::init:post', self);
  });

  //register block handler
  molecuel.on('mlcl::blocks::init:modules', function(blocks) {
    blocks.registerTypeHandler('collection', self.block);
  });

  return this;
};

/* ************************************************************************
 SINGLETON CLASS DEFINITION
 ************************************************************************ */
var instance = null;

/**
 * Singleton getInstance definition
 * @return singleton class
 */
var getInstance = function () {
  if (instance === null) {
    instance = new collections();
  }
  return instance;
};

/**
 * Load the definitions
 * @todo load from configuration
 */
collections.prototype.registerCollections = function registerCollections() {
  molecuel.emit('mlcl::collections::preGetDefinitions', this);
  var self = this;

  /**
   * Load collection definitions
   * @type {*}
   */
  var defFiles = fs.readdirSync(this.collectionDir);
  defFiles.forEach(function (entry) {
    var currentCollection = require(self.collectionDir + '/' + entry)();
    self.registerCollection(currentCollection);
  });
  molecuel.emit('mlcl::collections::postGetDefinitions', this);
};

/**
 * Register the collections
 * @param collection
 */
collections.prototype.registerCollection = function registerCollection(collection) {
  this.collectionRegistry[collection.name] = collection;
};

collections.prototype.apiGet = function apiGet(req, res, next) {
  var collections = getInstance();
  var collection = collections.getCollection(req.params.name);
  if(collection) {
    var currentQuery = new collquery(collections.elastic, new collections.getCollection(req.params.name), req.query);
    currentQuery.exec(function(err, result) {
      res.locals.api = result;
      next();
    });
  } else {
    next();
  }
};

/**
 * Return the definition of the collection query
 * @param name
 * @returns {*}
 */
collections.prototype.getCollection = function getCollection(name) {
  var collection = getInstance();
  return collection.collectionRegistry[name];
};

/**
 * Query the collections
 * @param name
 * @param options
 * @param callback
 */
collections.prototype.query = function query(name, options) {
  var currentQuery = new collquery(this.elastic, new this.getCollection(name), options);
  return currentQuery;
};

collections.prototype.exec = function exec(name, options, callback) {
  var self = this;
  var query = self.query(name, options);
  query.exec(function (err, result) {
    if(err) {
      return callback(err);
    }
    return self.processResult(result, callback);
  });
};

collections.prototype.processResult = function(query, result, callback) {
  var r = { total: 0, size: query.query.size, from: query.query.from, items: [] };
  r._meta = {
    module: 'collections',
    type: 'collection'
  };

  if (result && result.hits && result.hits.hits && result.hits.hits[0]) {
    r.total = result.hits.total;
    _.each(result.hits.hits, function(hit) {
      var source;
      if(hit._source) {
        source = hit._source;
        if(!source._id && hit._id) {
          source._id = hit._id
        }
      } else if(hit.fields) {
        source = hit.fields;
        if(!source._id && hit._id) {
          source._id = hit._id
        }
      }
      source._meta = {
        module: 'elements',
        type: hit._type
      };
      hit._view = { template: hit._type };
      r.items.push(source);
    });
  }
  return callback(null, r);
};

collections.prototype.block = function block(req, res, block, callback) {
  var self = getInstance();
  var params = {};
  var data = block.data;
  if(data) {
    if(data.params) {
      Object.keys(data.params).forEach(function(name) {
        var param = molecuel.utils.getVar(data.params[name], req, res);
        if(name == 'index') {
          param = self.elastic.getIndexName(param);
        }
        if(param) {
          params[name] = param;
        }
      });
      data.params = params;
    }

    var q = self.query(data.name, params);
    q.exec(function(err, result) {
      self.processResult(q, result, function(err, result) {
        _.extend(data, result);
        callback(null, result);
      });
    });
  } else {
    debug(block);
    callback(null);
  }
};

var init = function (m) {
  // store molecuel instance
  molecuel = m;
  return getInstance();
};

module.exports = init;
<commit_msg>bugfix: ignore result.hits undefined<commit_after>/**
 * Created by Dominic Böttger on 28.05.2014
 * INSPIRATIONlabs GmbH
 * http://www.inspirationlabs.com
 */
var _ = require('underscore');
var fs = require('fs');
var collquery = require ('./lib/query');
var debug = require('debug')('mlcl_collections');

var molecuel;

/**
 * This module serves the molecuel elements the type definition for database objects
 * @todo implement the dynamic generation of elements
 * @constructor
 */
var collections = function () {
  var self = this;

  this.collectionRegistry = {};
  // emit molecuel elements pre init event
  molecuel.emit('mlcl::collections::init:pre', self);

  /**
   * Schema directory config
   */
  if (molecuel.config.collections && molecuel.config.collections.collectionDir) {
    this.collectionDir = molecuel.config.collections.collectionDir;
  }

  /**
   * Execute after successful elasticsearch connection
   */
  molecuel.on('mlcl::elements::init:post', function (elements) {
    // add the access to all the db's and searchfunctionality
    self.elements = elements;
    self.elastic = elements.elastic;
    self.database = elements.database;
    self.registerCollections();
    molecuel.emit('mlcl::collections::init:post', self);
  });

  //register block handler
  molecuel.on('mlcl::blocks::init:modules', function(blocks) {
    blocks.registerTypeHandler('collection', self.block);
  });

  return this;
};

/* ************************************************************************
 SINGLETON CLASS DEFINITION
 ************************************************************************ */
var instance = null;

/**
 * Singleton getInstance definition
 * @return singleton class
 */
var getInstance = function () {
  if (instance === null) {
    instance = new collections();
  }
  return instance;
};

/**
 * Load the definitions
 * @todo load from configuration
 */
collections.prototype.registerCollections = function registerCollections() {
  molecuel.emit('mlcl::collections::preGetDefinitions', this);
  var self = this;

  /**
   * Load collection definitions
   * @type {*}
   */
  var defFiles = fs.readdirSync(this.collectionDir);
  defFiles.forEach(function (entry) {
    var currentCollection = require(self.collectionDir + '/' + entry)();
    self.registerCollection(currentCollection);
  });
  molecuel.emit('mlcl::collections::postGetDefinitions', this);
};

/**
 * Register the collections
 * @param collection
 */
collections.prototype.registerCollection = function registerCollection(collection) {
  this.collectionRegistry[collection.name] = collection;
};

collections.prototype.apiGet = function apiGet(req, res, next) {
  var collections = getInstance();
  var collection = collections.getCollection(req.params.name);
  if(collection) {
    var currentQuery = new collquery(collections.elastic, new collections.getCollection(req.params.name), req.query);
    currentQuery.exec(function(err, result) {
      res.locals.api = result;
      next();
    });
  } else {
    next();
  }
};

/**
 * Return the definition of the collection query
 * @param name
 * @returns {*}
 */
collections.prototype.getCollection = function getCollection(name) {
  var collection = getInstance();
  return collection.collectionRegistry[name];
};

/**
 * Query the collections
 * @param name
 * @param options
 * @param callback
 */
collections.prototype.query = function query(name, options) {
  var currentQuery = new collquery(this.elastic, new this.getCollection(name), options);
  return currentQuery;
};

collections.prototype.exec = function exec(name, options, callback) {
  var self = this;
  var query = self.query(name, options);
  query.exec(function (err, result) {
    if(err) {
      return callback(err);
    }
    return self.processResult(result, callback);
  });
};

collections.prototype.processResult = function(query, result, callback) {
  var r = { total: 0, size: query.query.size, from: query.query.from, items: [] };
  r._meta = {
    module: 'collections',
    type: 'collection'
  };

  if (result && result.hits && result.hits.hits && result.hits.hits[0]) {
    r.total = result.hits.total;
    _.each(result.hits.hits, function(hit) {
      var source;
      if(hit._source) {
        source = hit._source;
        if(!source._id && hit._id) {
          source._id = hit._id
        }
      } else if(hit.fields) {
        source = hit.fields;
        if(!source._id && hit._id) {
          source._id = hit._id
        }
      }
      if (source){
        source._meta = {
          module: 'elements',
          type: hit._type
        };
        hit._view = { template: hit._type };
        r.items.push(source);
      }
    });
  }
  return callback(null, r);
};

collections.prototype.block = function block(req, res, block, callback) {
  var self = getInstance();
  var params = {};
  var data = block.data;
  if(data) {
    if(data.params) {
      Object.keys(data.params).forEach(function(name) {
        var param = molecuel.utils.getVar(data.params[name], req, res);
        if(name == 'index') {
          param = self.elastic.getIndexName(param);
        }
        if(param) {
          params[name] = param;
        }
      });
      data.params = params;
    }

    var q = self.query(data.name, params);
    q.exec(function(err, result) {
      self.processResult(q, result, function(err, result) {
        _.extend(data, result);
        callback(null, result);
      });
    });
  } else {
    debug(block);
    callback(null);
  }
};

var init = function (m) {
  // store molecuel instance
  molecuel = m;
  return getInstance();
};

module.exports = init;
<|endoftext|>


<commit_before>// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').config();

// Require keystone
var keystone = require('keystone');

// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.

keystone.init({
	'name': 'turningpoint',
	'brand': 'turningpoint',

	'less': 'public',
	'static': 'public',
	'favicon': 'public/favicon.ico',
	'views': 'templates/views',
	'view engine': 'jade',

	'emails': 'templates/emails',

	'auto update': true,
	'session': true,
	//'session store': 'mongo',
	'auth': true,
	'user model': 'User',
});

// Load your project's Models
keystone.import('models');

// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js
keystone.set('locals', {
	_: require('lodash'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable,
});

// Load your project's Routes
keystone.set('routes', require('./routes'));

// Setup common locals for your emails. The following are required by Keystone's
// default email templates, you may remove them if you're using your own.
keystone.set('email locals', {
	logo_src: '/images/logo-email.gif',
	logo_width: 194,
	logo_height: 76,
	theme: {
		email_bg: '#f9f9f9',
		link_color: '#2697de',
		buttons: {
			color: '#fff',
			background_color: '#2697de',
			border_color: '#1a7cb7',
		},
	},
});

// Load your project's email test routes
keystone.set('email tests', require('./routes/emails'));

// Configure the navigation bar in Keystone's Admin UI
keystone.set('nav', {
	enquiries: 'enquiries',
	users: 'users',
});

// Start Keystone to connect to your database and initialise the web server

keystone.start();
<commit_msg>change signin redirect<commit_after>// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').config();

// Require keystone
var keystone = require('keystone');

// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.

keystone.init({
	'name': 'turningpoint',
	'brand': 'turningpoint',

	'less': 'public',
	'static': 'public',
	'favicon': 'public/favicon.ico',
	'views': 'templates/views',
	'view engine': 'jade',

	'emails': 'templates/emails',

	'auto update': true,
	'session': true,
	//'session store': 'mongo',
	'auth': true,
	'user model': 'User',
	'signin redirect': '/',
});

// Load your project's Models
keystone.import('models');

// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js
keystone.set('locals', {
	_: require('lodash'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable,
});

// Load your project's Routes
keystone.set('routes', require('./routes'));

// Setup common locals for your emails. The following are required by Keystone's
// default email templates, you may remove them if you're using your own.
keystone.set('email locals', {
	logo_src: '/images/logo-email.gif',
	logo_width: 194,
	logo_height: 76,
	theme: {
		email_bg: '#f9f9f9',
		link_color: '#2697de',
		buttons: {
			color: '#fff',
			background_color: '#2697de',
			border_color: '#1a7cb7',
		},
	},
});

// Load your project's email test routes
keystone.set('email tests', require('./routes/emails'));

// Configure the navigation bar in Keystone's Admin UI
keystone.set('nav', {
	enquiries: 'enquiries',
	users: 'users',
});

// Start Keystone to connect to your database and initialise the web server

keystone.start();
<|endoftext|>