<commit_before>'use strict';

var ko = require('knockout');
var $ = require('jquery');
var bootbox = require('bootbox');
require('bootstrap.growl');
var History = require('exports?History!history');

var $osf = require('js/osfHelpers');
// Enable knockout punches
ko.punches.enableAll();

// Disable IE Caching of JSON
$.ajaxSetup({ cache: false });

var Category = function(name, count, display){
    var self = this;

    self.name = name;
    self.count = count;
    self.display = display;

    self.url = ko.computed(function() {
        if (self.name === 'total') {
            return '';
        }
        return self.name + '/';
    });
};

var Tag = function(tagInfo){
    var self = this;
    self.name = tagInfo.key;
    self.count = tagInfo.doc_count;
};

var User = function(result){
    var self = this;
    self.category = result.category;
    self.gravatarUrl = ko.observable('');
    self.social = result.social;
    self.job_title = result.job_title;
    self.job = result.job;
    self.degree = result.degree;
    self.school = result.school;
    self.url = result.url;
    self.user = result.user;

    $.ajax('/api/v1'+ result.url).success(function(data){
        if (typeof data.profile !== 'undefined') {
            self.gravatarUrl(data.profile.gravatar_url);
        }
    });
};

var ViewModel = function(params) {
    var self = this;
    self.params = params || {};
    self.queryUrl = self.params.url;
    self.appURL = self.params.appURL;

    self.tag = ko.observable('');
    self.stateJustPushed = true;
    self.query = ko.observable('');
    self.category = ko.observable({});
    self.tags = ko.observableArray([]);
    self.tagMaxCount = ko.observable(1);
    self.currentPage = ko.observable(1);
    self.totalResults = ko.observable(0);
    self.results = ko.observableArray([]);
    self.searching = ko.observable(false);
    self.resultsPerPage = ko.observable(10);
    self.categories = ko.observableArray([]);
    self.shareCategory = ko.observable('');
    self.searchStarted = ko.observable(false);
    self.showSearch = true;
    self.showClose = false;
    self.searchCSS = ko.observable('active');
    self.onSearchPage = true;

    // Maintain compatibility with hiding search bar elsewhere on the site
    self.toggleSearch = function() {
    };

    self.allCategories = ko.pureComputed(function(){
        if(self.shareCategory()){
            return self.categories().concat(self.shareCategory());
        }
        return self.categories().concat(new Category('SHARE', 0, 'SHARE'));
    });

    self.totalCount = ko.pureComputed(function() {
        if (self.categories().length === 0 || self.categories()[0] === undefined) {
            return 0;
        }

        return self.categories()[0].count;
    });

    self.totalPages = ko.pureComputed(function() {
        var resultsCount = Math.max(self.resultsPerPage(),1); // No Divide by Zero
        var countOfPages = Math.ceil(self.totalResults() / resultsCount);
        return countOfPages;
    });

    self.nextPageExists = ko.pureComputed(function() {
        return ((self.totalPages() > 1) && (self.currentPage() < self.totalPages()));
    });

    self.prevPageExists = ko.pureComputed(function() {
        return self.totalPages() > 1 && self.currentPage() > 1;
    });

    self.currentIndex = ko.pureComputed(function() {
        return Math.max(self.resultsPerPage() * (self.currentPage()-1),0);
    });

    self.navLocation = ko.pureComputed(function() {
        return 'Page ' + self.currentPage() + ' of ' + self.totalPages();
    });

    self.queryObject = ko.pureComputed(function(){
        var TITLE_BOOST = '4';
        var DESCRIPTION_BOOST = '1.2';
        var JOB_SCHOOL_BOOST = '1';
        var ALL_JOB_SCHOOL_BOOST = '0.125';

        var fields = [
            '_all',
            'title^' + TITLE_BOOST,
            'description^' + DESCRIPTION_BOOST,
            'job^' + JOB_SCHOOL_BOOST,
            'school^' + JOB_SCHOOL_BOOST,
            'all_jobs^' + ALL_JOB_SCHOOL_BOOST,
            'all_schools^' + ALL_JOB_SCHOOL_BOOST
        ];
        return {
            'query_string': {
                'default_field': '_all',
                'fields': fields,
                'query': self.query(),
                'analyze_wildcard': true,
                'lenient': true
            }
        };
    });

    self.fullQuery = ko.pureComputed(function() {
        return {
            'filtered': {
                'query': self.queryObject()
            }
        };
    });

    self.sortCategories = function(a, b) {
        if(a.name === 'Total') {
            return -1;
        } else if (b.name === 'Total') {
            return 1;
        }
        return a.count >  b.count ? -1 : 1;
    };

    self.help = function() {
        bootbox.dialog({
            title: 'Search help',
            message: '
Queries
'+
                '
Search uses the Lucene search syntax. ' +
                'This gives you many options, but can be very simple as well. ' +
                'Examples of valid searches include:' +
                '

repro*
' +
                '
brian AND title:many
' +
                '
tags:(psychology)

' +
                '
'
        });
    };

    self.filter = function(alias) {
        self.searchStarted(false);
        self.currentPage(1);
        self.category(alias);
        if (alias.name === 'SHARE') {
            document.location = '/share/?' + $.param({q: self.query()});
        } else {
            self.search();
        }
    };

    self._makeTagString = function(tagName) {
        return 'tags:("' + tagName + '")';
    };
    self.addTag = function(tagName) {
        var tagString = self._makeTagString(tagName);
        var query = self.query();
        if (query.indexOf(tagString) === -1) {
            if (self.query() !== '') {
                query += ' AND ';
            }
            query += tagString;
            self.query(query);
            self.onUpdateTags();
        }
    };
    self.removeTag = function(tagName, _, e) {
        e.stopPropagation();
        var query = self.query();
        var tagRegExp = /(?:AND)?\s*tags\:\([\'\"](.+?)[\'\"]\)/g;
        var matches = query.match(tagRegExp);
        var dirty = false;
        while (matches.length) {
            var match = matches.pop();
            if (match.match(tagName).length) {
                query = query.replace(match, '');
                dirty = true;
            }
        }
        if (dirty) {
            self.query(query);
            self.onUpdateTags();
        }
    };
    self.onUpdateTags = function() {
        self.category(new Category('total', 0, 'Total'));
        self.currentPage(1);
        self.search();
    };

    self.submit = function() {
        $('#searchPageFullBar').blur().focus();
        self.searchStarted(false);
        self.totalResults(0);
        self.currentPage(1);
        self.search();
    };

    self.search = function(noPush, validate) {

        // Check for NOTs and ANDs put spaces before the ones that don't have spaces
        var query = self.query().replace(/\s?NOT tags:/g, ' NOT tags:');
        query = query.replace(/\s?AND tags:/g, ' AND tags:');
        self.query(query);

        var jsonData = {'query': self.fullQuery(), 'from': self.currentIndex(), 'size': self.resultsPerPage()};
        var url = self.queryUrl + self.category().url();

        $osf.postJSON(url, jsonData).success(function(data) {

            //Clear out our variables
            self.tags([]);
            self.tagMaxCount(1);
            self.results.removeAll();
            self.categories.removeAll();
            self.shareCategory('');

            data.results.forEach(function(result){
                if(result.category === 'user'){
                    self.results.push(new User(result));
                }
                else {
                    if(typeof result.url !== 'undefined'){
                        result.wikiUrl = result.url+'wiki/';
                        result.filesUrl = result.url+'files/';
                    }

                    self.results.push(result);
                }
                if(result.category === 'registration'){
                    result.dateRegistered = new $osf.FormattableDate(result.date_registered);
                }
            });

            //Load our categories
            var categories = data.counts;
            $.each(categories, function(key, value){
                if (value === null) {
                    value = 0;
                }
                self.categories.push(new Category(key, value, data.typeAliases[key]));
            });

            self.categories(self.categories().sort(self.sortCategories));

            // If our category is named attempt to load its total else set it to the total total
            if (self.category().name !== undefined) {
                self.totalResults(data.counts[self.category().name] || 0);
            } else {
                self.totalResults(self.self.categories()[0].count);
            }

            // Load up our tags
            $.each(data.tags, function(key, value){
                self.tags.push(new Tag(value));
                self.tagMaxCount(Math.max(self.tagMaxCount(), value.doc_count));
            });

            self.searchStarted(true);

            if (validate) {
                self.validateSearch();
            }

            if (!noPush) {
                self.pushState();
            }

            $osf.postJSON('/api/v1/share/search/?count&v=1', jsonData).success(function(data) {
                self.shareCategory(new Category('SHARE', data.count, 'SHARE'));
            });

        }).fail(function(response){
            self.totalResults(0);
            self.currentPage(0);
            self.results([]);
            self.tags([]);
            self.categories([]);
            self.searchStarted(false);
            $osf.handleJSONError(response);
        });

    };

    self.paginate = function(val) {
        window.scrollTo(0, 0);
        self.currentPage(self.currentPage()+val);
        self.search();
    };

    self.pagePrev = self.paginate.bind(self, -1);
    self.pageNext = self.paginate.bind(self, 1);

    //History JS callback
    self.pageChange = function() {
        if (self.stateJustPushed) {
            self.stateJustPushed = false;
            return;
        }

        self.loadState();

        self.search(true);
    };

    //Ensure that the first url displays properly
    self.validateSearch = function() {
        var possibleCategories;
        if (self.category().name !== undefined) {
            possibleCategories = $.map(self.categories().filter(function(category) {
                return category.count > 0;
            }), function(category) {
                return category.name;
            });

            if (possibleCategories.indexOf(self.category().name) === -1 && possibleCategories.length !== 0) {
                self.filter(self.categories()[0]);
                return self.search(true);
            }
        }
        if (self.currentPage() > self.totalPages() && self.currentPage() !== 1) {
            self.currentPage(self.totalPages());
            return self.search(true);
        }
    };

    //Load state from History JS
    self.loadState = function() {
        var state = History.getState().data;
        self.currentPage(state.page || 1);
        self.setCategory(state.filter);
        self.query(state.query || '');
    };

    //Push a new state to History
    self.pushState = function() {
        var state = {
            filter: '',
            query: self.query(),
            page: self.currentPage(),
            scrollTop: $(window).scrollTop()
        };

        var url = '?q=' + self.query();

        if (self.category().name !== undefined && self.category().url() !== '') {
            state.filter = self.category().name;
            url += ('&filter=' + self.category().name);
        }

        url += ('&page=' + self.currentPage());

        //Indicate that we've just pushed a state so the
        //Call back does not process this push as a state change
        self.stateJustPushed = true;
        History.pushState(state, 'OSF | Search', url);
    };

    self.setCategory = function(cat) {
        if (cat !== undefined && cat !== null && cat !== '') {
            self.category(new Category(cat, 0, cat.charAt(0).toUpperCase() + cat.slice(1) + 's'));
        } else {
            self.category(new Category('total', 0, 'Total'));
        }
    };

};

function Search(selector, url, appURL) {
    // Initialization code
    var self = this;

    self.viewModel = new ViewModel({'url': url, 'appURL': appURL});
    History.Adapter.bind(window, 'statechange', self.viewModel.pageChange);

    var data = {
        query: $osf.urlParams().q,
        page: Number($osf.urlParams().page),
        scrollTop: 0,
        filter: $osf.urlParams().filter
    };
    //Ensure our state keeps its URL paramaters
    History.replaceState(data, 'OSF | Search', location.search);
    //Set out observables from the newly replaced state
    self.viewModel.loadState();
    //Preform search from url params
    self.viewModel.search(true, true);

    $osf.applyBindings(self.viewModel, selector);
}

module.exports = Search;
<commit_msg>Handle queries w/ multiple tags<commit_after>'use strict';

var ko = require('knockout');
var $ = require('jquery');
var bootbox = require('bootbox');
require('bootstrap.growl');
var History = require('exports?History!history');

var $osf = require('js/osfHelpers');
// Enable knockout punches
ko.punches.enableAll();

// Disable IE Caching of JSON
$.ajaxSetup({ cache: false });

var Category = function(name, count, display){
    var self = this;

    self.name = name;
    self.count = count;
    self.display = display;

    self.url = ko.computed(function() {
        if (self.name === 'total') {
            return '';
        }
        return self.name + '/';
    });
};

var Tag = function(tagInfo){
    var self = this;
    self.name = tagInfo.key;
    self.count = tagInfo.doc_count;
};

var User = function(result){
    var self = this;
    self.category = result.category;
    self.gravatarUrl = ko.observable('');
    self.social = result.social;
    self.job_title = result.job_title;
    self.job = result.job;
    self.degree = result.degree;
    self.school = result.school;
    self.url = result.url;
    self.user = result.user;

    $.ajax('/api/v1'+ result.url).success(function(data){
        if (typeof data.profile !== 'undefined') {
            self.gravatarUrl(data.profile.gravatar_url);
        }
    });
};

var ViewModel = function(params) {
    var self = this;
    self.params = params || {};
    self.queryUrl = self.params.url;
    self.appURL = self.params.appURL;

    self.tag = ko.observable('');
    self.stateJustPushed = true;
    self.query = ko.observable('');
    self.category = ko.observable({});
    self.tags = ko.observableArray([]);
    self.tagMaxCount = ko.observable(1);
    self.currentPage = ko.observable(1);
    self.totalResults = ko.observable(0);
    self.results = ko.observableArray([]);
    self.searching = ko.observable(false);
    self.resultsPerPage = ko.observable(10);
    self.categories = ko.observableArray([]);
    self.shareCategory = ko.observable('');
    self.searchStarted = ko.observable(false);
    self.showSearch = true;
    self.showClose = false;
    self.searchCSS = ko.observable('active');
    self.onSearchPage = true;

    // Maintain compatibility with hiding search bar elsewhere on the site
    self.toggleSearch = function() {
    };

    self.allCategories = ko.pureComputed(function(){
        if(self.shareCategory()){
            return self.categories().concat(self.shareCategory());
        }
        return self.categories().concat(new Category('SHARE', 0, 'SHARE'));
    });

    self.totalCount = ko.pureComputed(function() {
        if (self.categories().length === 0 || self.categories()[0] === undefined) {
            return 0;
        }

        return self.categories()[0].count;
    });

    self.totalPages = ko.pureComputed(function() {
        var resultsCount = Math.max(self.resultsPerPage(),1); // No Divide by Zero
        var countOfPages = Math.ceil(self.totalResults() / resultsCount);
        return countOfPages;
    });

    self.nextPageExists = ko.pureComputed(function() {
        return ((self.totalPages() > 1) && (self.currentPage() < self.totalPages()));
    });

    self.prevPageExists = ko.pureComputed(function() {
        return self.totalPages() > 1 && self.currentPage() > 1;
    });

    self.currentIndex = ko.pureComputed(function() {
        return Math.max(self.resultsPerPage() * (self.currentPage()-1),0);
    });

    self.navLocation = ko.pureComputed(function() {
        return 'Page ' + self.currentPage() + ' of ' + self.totalPages();
    });

    self.queryObject = ko.pureComputed(function(){
        var TITLE_BOOST = '4';
        var DESCRIPTION_BOOST = '1.2';
        var JOB_SCHOOL_BOOST = '1';
        var ALL_JOB_SCHOOL_BOOST = '0.125';

        var fields = [
            '_all',
            'title^' + TITLE_BOOST,
            'description^' + DESCRIPTION_BOOST,
            'job^' + JOB_SCHOOL_BOOST,
            'school^' + JOB_SCHOOL_BOOST,
            'all_jobs^' + ALL_JOB_SCHOOL_BOOST,
            'all_schools^' + ALL_JOB_SCHOOL_BOOST
        ];
        return {
            'query_string': {
                'default_field': '_all',
                'fields': fields,
                'query': self.query(),
                'analyze_wildcard': true,
                'lenient': true
            }
        };
    });

    self.fullQuery = ko.pureComputed(function() {
        return {
            'filtered': {
                'query': self.queryObject()
            }
        };
    });

    self.sortCategories = function(a, b) {
        if(a.name === 'Total') {
            return -1;
        } else if (b.name === 'Total') {
            return 1;
        }
        return a.count >  b.count ? -1 : 1;
    };

    self.help = function() {
        bootbox.dialog({
            title: 'Search help',
            message: '
Queries
'+
                '
Search uses the Lucene search syntax. ' +
                'This gives you many options, but can be very simple as well. ' +
                'Examples of valid searches include:' +
                '

repro*
' +
                '
brian AND title:many
' +
                '
tags:(psychology)

' +
                '
'
        });
    };

    self.filter = function(alias) {
        self.searchStarted(false);
        self.currentPage(1);
        self.category(alias);
        if (alias.name === 'SHARE') {
            document.location = '/share/?' + $.param({q: self.query()});
        } else {
            self.search();
        }
    };

    self._makeTagString = function(tagName) {
        return 'tags:("' + tagName + '")';
    };
    self.addTag = function(tagName) {
        var tagString = self._makeTagString(tagName);
        var query = self.query();
        if (query.indexOf(tagString) === -1) {
            if (self.query() !== '') {
                query += ' AND ';
            }
            query += tagString;
            self.query(query);
            self.onUpdateTags();
        }
    };
    self.removeTag = function(tagName, _, e) {
        e.stopPropagation();
        var query = self.query();
        var tagRegExp = /(?:AND)?\s*tags\:\([\'\"](.+?)[\'\"]\)/g;
        var matches = query.match(tagRegExp);
        var dirty = false;
        while (matches.length) {
            var match = matches.pop();
            if ((match.match(tagName) || []).length) {
                query = query.replace(match, '');
                dirty = true;
            }
        }
        if (dirty) {
            self.query(query);
            self.onUpdateTags();
        }
    };
    self.onUpdateTags = function() {
        self.category(new Category('total', 0, 'Total'));
        self.currentPage(1);
        self.search();
    };

    self.submit = function() {
        $('#searchPageFullBar').blur().focus();
        self.searchStarted(false);
        self.totalResults(0);
        self.currentPage(1);
        self.search();
    };

    self.search = function(noPush, validate) {

        // Check for NOTs and ANDs put spaces before the ones that don't have spaces
        var query = self.query().replace(/\s?NOT tags:/g, ' NOT tags:');
        query = query.replace(/\s?AND tags:/g, ' AND tags:');
        self.query(query);

        var jsonData = {'query': self.fullQuery(), 'from': self.currentIndex(), 'size': self.resultsPerPage()};
        var url = self.queryUrl + self.category().url();

        $osf.postJSON(url, jsonData).success(function(data) {

            //Clear out our variables
            self.tags([]);
            self.tagMaxCount(1);
            self.results.removeAll();
            self.categories.removeAll();
            self.shareCategory('');

            data.results.forEach(function(result){
                if(result.category === 'user'){
                    self.results.push(new User(result));
                }
                else {
                    if(typeof result.url !== 'undefined'){
                        result.wikiUrl = result.url+'wiki/';
                        result.filesUrl = result.url+'files/';
                    }

                    self.results.push(result);
                }
                if(result.category === 'registration'){
                    result.dateRegistered = new $osf.FormattableDate(result.date_registered);
                }
            });

            //Load our categories
            var categories = data.counts;
            $.each(categories, function(key, value){
                if (value === null) {
                    value = 0;
                }
                self.categories.push(new Category(key, value, data.typeAliases[key]));
            });

            self.categories(self.categories().sort(self.sortCategories));

            // If our category is named attempt to load its total else set it to the total total
            if (self.category().name !== undefined) {
                self.totalResults(data.counts[self.category().name] || 0);
            } else {
                self.totalResults(self.self.categories()[0].count);
            }

            // Load up our tags
            $.each(data.tags, function(key, value){
                self.tags.push(new Tag(value));
                self.tagMaxCount(Math.max(self.tagMaxCount(), value.doc_count));
            });

            self.searchStarted(true);

            if (validate) {
                self.validateSearch();
            }

            if (!noPush) {
                self.pushState();
            }

            $osf.postJSON('/api/v1/share/search/?count&v=1', jsonData).success(function(data) {
                self.shareCategory(new Category('SHARE', data.count, 'SHARE'));
            });

        }).fail(function(response){
            self.totalResults(0);
            self.currentPage(0);
            self.results([]);
            self.tags([]);
            self.categories([]);
            self.searchStarted(false);
            $osf.handleJSONError(response);
        });

    };

    self.paginate = function(val) {
        window.scrollTo(0, 0);
        self.currentPage(self.currentPage()+val);
        self.search();
    };

    self.pagePrev = self.paginate.bind(self, -1);
    self.pageNext = self.paginate.bind(self, 1);

    //History JS callback
    self.pageChange = function() {
        if (self.stateJustPushed) {
            self.stateJustPushed = false;
            return;
        }

        self.loadState();

        self.search(true);
    };

    //Ensure that the first url displays properly
    self.validateSearch = function() {
        var possibleCategories;
        if (self.category().name !== undefined) {
            possibleCategories = $.map(self.categories().filter(function(category) {
                return category.count > 0;
            }), function(category) {
                return category.name;
            });

            if (possibleCategories.indexOf(self.category().name) === -1 && possibleCategories.length !== 0) {
                self.filter(self.categories()[0]);
                return self.search(true);
            }
        }
        if (self.currentPage() > self.totalPages() && self.currentPage() !== 1) {
            self.currentPage(self.totalPages());
            return self.search(true);
        }
    };

    //Load state from History JS
    self.loadState = function() {
        var state = History.getState().data;
        self.currentPage(state.page || 1);
        self.setCategory(state.filter);
        self.query(state.query || '');
    };

    //Push a new state to History
    self.pushState = function() {
        var state = {
            filter: '',
            query: self.query(),
            page: self.currentPage(),
            scrollTop: $(window).scrollTop()
        };

        var url = '?q=' + self.query();

        if (self.category().name !== undefined && self.category().url() !== '') {
            state.filter = self.category().name;
            url += ('&filter=' + self.category().name);
        }

        url += ('&page=' + self.currentPage());

        //Indicate that we've just pushed a state so the
        //Call back does not process this push as a state change
        self.stateJustPushed = true;
        History.pushState(state, 'OSF | Search', url);
    };

    self.setCategory = function(cat) {
        if (cat !== undefined && cat !== null && cat !== '') {
            self.category(new Category(cat, 0, cat.charAt(0).toUpperCase() + cat.slice(1) + 's'));
        } else {
            self.category(new Category('total', 0, 'Total'));
        }
    };

};

function Search(selector, url, appURL) {
    // Initialization code
    var self = this;

    self.viewModel = new ViewModel({'url': url, 'appURL': appURL});
    History.Adapter.bind(window, 'statechange', self.viewModel.pageChange);

    var data = {
        query: $osf.urlParams().q,
        page: Number($osf.urlParams().page),
        scrollTop: 0,
        filter: $osf.urlParams().filter
    };
    //Ensure our state keeps its URL paramaters
    History.replaceState(data, 'OSF | Search', location.search);
    //Set out observables from the newly replaced state
    self.viewModel.loadState();
    //Preform search from url params
    self.viewModel.search(true, true);

    $osf.applyBindings(self.viewModel, selector);
}

module.exports = Search;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Navbar, NavItem, Nav } from 'react-bootstrap'
import './NavMenu.css'

class NavMenu extends Component {
  render() {
    return (
      <Navbar inverse collapseOnSelect>
        <Navbar.Header>
          <Navbar.Toggle />
        </Navbar.Header>
        <Navbar.Collapse>

            {this.props.menuButtons.map(function(item, index) {
              return(
                <NavItem eventKey={index} href="#">{item.text}

                </NavItem>
              )
            })}

        </Navbar.Collapse>
      </Navbar>
    )
  }
}

export default NavMenu<commit_msg>added menu item click handler<commit_after>import React, { Component } from 'react';
import { Navbar, NavItem, Nav } from 'react-bootstrap'
import './NavMenu.css'

class NavMenu extends Component {
  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick(evt) {
    console.log(evt)
  }
  render() {
    return (
      <Navbar inverse collapseOnSelect onSelect={this.handleClick}>
        <Navbar.Header>
          <Navbar.Toggle />
        </Navbar.Header>
        <Navbar.Collapse>

            {this.props.menuButtons.map(function(item, index) {
              return(
                <NavItem eventKey={index} href="#">{item.text}

                </NavItem>
              )
            })}

        </Navbar.Collapse>
      </Navbar>
    )
  }
}

export default NavMenu<|endoftext|>


<commit_before><commit_msg>Generate TOC for each page on brochure<commit_after><|endoftext|>


<commit_before>/*jslint vars: false, browser: true, nomen: true */
/*global MOOC: true, Backbone, $, _ */

if (_.isUndefined(window.MOOC)) {
    window.MOOC = {};
}

MOOC.models = {};

MOOC.models.Activity = Backbone.Model.extend({
    defaults: {
        kqs: [],
    },

    url: function () {
        return MOOC.ajax.getAbsoluteUrl('activity/') + this.get('id') + '/';
    },

    addKQ: function (kq) {
        if (!_.include(this.get('kqs'), kq)) {
            this.set('kqs', _.union(this.get('kqs'), [kq]));
            this.save();
        }
    },

    hasKQ: function (kq) {
        return _.include(this.get('kqs'), '' + kq);
    }
});

MOOC.models.Option = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            optiontype: 't',
            x: 0,
            y: 0,
            width: 100,
            height: 12,
            solution: null
        };
    },

    isCorrect: function (reply) {
        "use strict";
        var solution = this.get('solution'),
            optiontype = this.get('optiontype');

        if (_.isUndefined(solution) || solution === null) {
            return;
        }

        if (optiontype === 't') {
            return solution === reply.get('value');
        } else {
            if (solution === 'True') {
                return reply.get('value') === true;
            } else {
                return reply.get('value') === false;
            }
        }
    }
});

MOOC.models.OptionList  = Backbone.Collection.extend({
    model: MOOC.models.Option
});

MOOC.models.Question = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            lastFrame: null, // of the KnowledgeQuantum's video
            solution: null,
            optionList: null,
            answer: null
        };
    },

    isCorrect: function () {
        "use strict";
        var answer = this.get("answer");
        if (_.isUndefined(answer) || answer === null) {
            return;
        }

        return this.get('optionList').all(function (opt) {
            return opt.isCorrect(answer.getReply(opt.get('id')));
        });
    }
});

/* An answer is a student submission to a question */
MOOC.models.Answer = Backbone.Model.extend({
    defaults: {
        date: null,
        replyList: null
    },

    /* Return a reply which option is opt_id or null otherwise */
    getReply: function (opt_id) {
        "use strict";
        var replies = this.get('replyList');
        if (replies) {
            return replies.find(function (reply) {
                return reply.get('option') === opt_id;
            });
        } else {
            return null;
        }
    }
});

/* A reply is a student value for an option */
MOOC.models.Reply = Backbone.Model.extend({
    defaults: {
        option: null,
        value: null
    }
});

MOOC.models.ReplyList = Backbone.Collection.extend({
    model: MOOC.models.Reply
});

MOOC.models.KnowledgeQuantum = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            order: -1,
            title: null,
            videoID: null,
            teacher_comments: null,
            supplementary_material: null,
            question: null, // Optional
            questionInstance: null,
            completed: false,
            correct: null
        };
    }
});

MOOC.models.KnowledgeQuantumList  = Backbone.Collection.extend({
    model: MOOC.models.KnowledgeQuantum,

    comparator: function (kq) {
        "use strict";
        return kq.get("order");
    },

    getByPosition: function (position) {
        "use strict";
        return this.find(function (kq) {
            return position === kq.get("order");
        });
    }
});

MOOC.models.Unit = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            order: -1,
            knowledgeQuantumList: null,
            title: ""
        };
    },

    calculateProgress: function (conditions) {
        "use strict";
        var kqs = this.get("knowledgeQuantumList").length,
            progress = this.get("knowledgeQuantumList").where(conditions);
        return (progress.length * 100) / kqs;
    }
});

MOOC.models.UnitList = Backbone.Collection.extend({
    model: MOOC.models.Unit,

    getByKQ: function (kqID) {
        "use strict";
        return this.find(function (unit) {
            var kq = unit.get("knowledgeQuantumList");
            if (_.isNull(kq)) {
                return false;
            }
            kq = kq.get(kqID);
            return !_.isUndefined(kq);
        });
    }
});

MOOC.models.course = new MOOC.models.UnitList();
<commit_msg>Make models pass jslint<commit_after>/*jslint vars: false, browser: true, nomen: true */
/*global MOOC: true, Backbone, $, _ */

if (_.isUndefined(window.MOOC)) {
    window.MOOC = {};
}

MOOC.models = {};

MOOC.models.Activity = Backbone.Model.extend({
    defaults: {
        kqs: []
    },

    url: function () {
        "use strict";
        return MOOC.ajax.getAbsoluteUrl('activity/') + this.get('id') + '/';
    },

    addKQ: function (kq) {
        "use strict";
        if (!_.include(this.get('kqs'), kq)) {
            this.set('kqs', _.union(this.get('kqs'), [kq]));
            this.save();
        }
    },

    hasKQ: function (kq) {
        "use strict";
        return _.include(this.get('kqs'), String(kq));
    }
});

MOOC.models.Option = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            optiontype: 't',
            x: 0,
            y: 0,
            width: 100,
            height: 12,
            solution: null
        };
    },

    isCorrect: function (reply) {
        "use strict";
        var solution = this.get('solution'),
            optiontype = this.get('optiontype'),
            result;

        if (_.isUndefined(solution) || solution === null) {
            return;
        }

        if (optiontype === 't') {
            result = solution === reply.get('value');
        } else {
            if (solution === 'True') {
                result = reply.get('value') === true;
            } else {
                result = reply.get('value') === false;
            }
        }

        return result;
    }
});

MOOC.models.OptionList  = Backbone.Collection.extend({
    model: MOOC.models.Option
});

MOOC.models.Question = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            lastFrame: null, // of the KnowledgeQuantum's video
            solution: null,
            optionList: null,
            answer: null
        };
    },

    isCorrect: function () {
        "use strict";
        var answer = this.get("answer");
        if (_.isUndefined(answer) || answer === null) {
            return;
        }

        return this.get('optionList').all(function (opt) {
            return opt.isCorrect(answer.getReply(opt.get('id')));
        });
    }
});

/* An answer is a student submission to a question */
MOOC.models.Answer = Backbone.Model.extend({
    defaults: {
        date: null,
        replyList: null
    },

    /* Return a reply which option is opt_id or null otherwise */
    getReply: function (opt_id) {
        "use strict";
        var replies = this.get('replyList'),
            result = null;
        if (replies) {
            result = replies.find(function (reply) {
                return reply.get('option') === opt_id;
            });
        }
        return result;
    }
});

/* A reply is a student value for an option */
MOOC.models.Reply = Backbone.Model.extend({
    defaults: {
        option: null,
        value: null
    }
});

MOOC.models.ReplyList = Backbone.Collection.extend({
    model: MOOC.models.Reply
});

MOOC.models.KnowledgeQuantum = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            order: -1,
            title: null,
            videoID: null,
            teacher_comments: null,
            supplementary_material: null,
            question: null, // Optional
            questionInstance: null,
            completed: false,
            correct: null
        };
    }
});

MOOC.models.KnowledgeQuantumList  = Backbone.Collection.extend({
    model: MOOC.models.KnowledgeQuantum,

    comparator: function (kq) {
        "use strict";
        return kq.get("order");
    },

    getByPosition: function (position) {
        "use strict";
        return this.find(function (kq) {
            return position === kq.get("order");
        });
    }
});

MOOC.models.Unit = Backbone.Model.extend({
    defaults: function () {
        "use strict";
        return {
            order: -1,
            knowledgeQuantumList: null,
            title: ""
        };
    },

    calculateProgress: function (conditions) {
        "use strict";
        var kqs = this.get("knowledgeQuantumList").length,
            progress = this.get("knowledgeQuantumList").where(conditions);
        return (progress.length * 100) / kqs;
    }
});

MOOC.models.UnitList = Backbone.Collection.extend({
    model: MOOC.models.Unit,

    getByKQ: function (kqID) {
        "use strict";
        return this.find(function (unit) {
            var kq = unit.get("knowledgeQuantumList");
            if (_.isNull(kq)) {
                return false;
            }
            kq = kq.get(kqID);
            return !_.isUndefined(kq);
        });
    }
});

MOOC.models.course = new MOOC.models.UnitList();
<|endoftext|>


<commit_before>var
  async = require('async'),
  _ = require('lodash'),
  stringify = require('json-stable-stringify'),
  Router = require('router'),
  bodyParser = require('body-parser'),
  finalhandler = require('finalhandler'),
  RequestObject = require('../core/models/requestObject'),
  ResponseListener = require('../core/responseListener');

module.exports = function RouterController (kuzzle) {
  this.router = null;
  this.controllers = ['write', 'read', 'subscribe', 'admin', 'bulk'];
  this.kuzzle = kuzzle;
  this.listener = new ResponseListener(this.kuzzle, this.kuzzle.config.queues.workerWriteResponseQueue);

  /**
   * Initializes the HTTP routes for the Kuzzle REST API.
   */
  this.initRouterHttp = function () {
    var
      api = new Router(),
      coverage;

    this.router = new Router();

    // create and mount a new router for the coverage API
    if (process.env.FEATURE_COVERAGE === '1') {
      coverage = require('istanbul-middleware');
      this.router.use('/coverage', coverage.createHandler({ resetOnGet: true }));
    }

    // create and mount a new router for our API
    this.router.use('/api/', api);

    // add a body parsing middleware to our API
    api.use(bodyParser.json());

    // Simple hello world to let know to the user that kuzzle is running
    api.get('/', function (request, response) {
      response.writeHead('Access-Control-Allow-Origin', '*');
      response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');
      response.writeHead(200, {'Content-Type': 'application/json'});
      response.end(stringify({error: null, result: 'Hello from Kuzzle :)'}));
    });

    api.post('/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'putMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.get('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'getMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_search', function (request, response) {
      var params = {
        controller: 'read',
        action: 'search'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection/_query', function (request, response) {
      var params = {
        controller: 'write',
        action: 'deleteByQuery'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_count', function (request, response) {
      var params = {
        controller: 'read',
        action: 'count'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/:id/_:action', function (request, response) {
      var params = {
        controller: 'write'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'createOrUpdate'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection', function (request, response) {
      var params = {
        controller: 'write',
        action: 'create'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.get('/:collection/:id', function (request, response) {
      var params = {
        controller: 'read',
        action: 'get'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'delete'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'deleteCollection'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

  };

  /**
   * Forward incoming REST requests to the HTTP routes created by the
   * initRouterHttp function.
   *
   * @param request transmitted through the REST API
   * @param response is the HTTP connection handler
   */
  this.routeHttp = function (request, response) {
    kuzzle.pluginsManager.trigger('log:silly', 'Handle HTTP request');

    this.router(request, response, finalhandler(request, response));
  };

  /**
   * Handles requests coming from websocket connections
   *
   * @param {Object} socket client
   */
  this.routeWebsocket = function (socket) {
    var
      routerCtrl = this,
      connection = {type: 'websocket', id: socket.id};

    async.each(routerCtrl.controllers, function recordSocketListener (controller) {
      socket.on(controller, function (data) {
        var requestObject = new RequestObject(data, {controller: controller}, 'websocket');

        kuzzle.pluginsManager.trigger('log:silly', 'Handle Websocket for controller ' + controller);
        kuzzle.router.listener.add(requestObject, connection);

        // Execute the funnel. Forward any non-empty response to the user.
        kuzzle.funnel.execute(requestObject, connection)
          .then(function onExecuteSuccess (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function onExecuteError(error) {
            kuzzle.emit(requestObject.controller + ':websocket:funnel:reject', error);
            kuzzle.pluginsManager.trigger('log:error', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      });
    });

    // handles socket disconnections
    socket.on('disconnect', function () {
      kuzzle.pluginsManager.trigger('websocket:disconnect', 'A client is disconnected');
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });

    // handles socket crashes
    socket.on('error', function (error) {
      kuzzle.pluginsManager.trigger('websocket:error', error);
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });
  };

  /**
   * Handles requests coming from MQ protocols: AMQP, MQTT & STOMP
   *
   */
  this.routeMQListener = function () {
    async.each(this.controllers, function recordMQListener (controller) {
      kuzzle.services.list.mqBroker.listenExchange(controller+'.*.*', function handleMQMessage(msg) {
        var
          connection = null,
          data,
          requestObject,
          rawContent,
          routingArray = msg.fields.routingKey.split('.'),
          // routingArray[0] is the controller, which we already have in the 'controller' variable
          collection = routingArray[1],
          action = routingArray[2];

        if (!(msg.content instanceof Buffer)) {
          rawContent = msg.content.toString();
        }
        else {
          rawContent = (new Buffer(msg.content)).toString();
        }

        try {
          data = JSON.parse(rawContent);
        }
        catch (e) {
          kuzzle.pluginsManager.trigger('log:error', {message: 'Parse error', error: e});
          return false;
        }

        kuzzle.pluginsManager.trigger('log:silly', 'Handle MQ input' + msg.fields.routingKey);

        // For MQTT messages, we do not have a replyTo header like with AMQP or STOMP
        // => MQTT client has to send its mqtt client id and subscribe to the topic exchange mqtt.<clientId>
        //    to get feedback from Kuzzle.
        if (msg.properties && msg.properties.replyTo) {
          connection = {type: 'amq', id: data.clientId, replyTo: msg.properties.replyTo};
        }
        else {
          connection = {type: 'mqtt', id: data.clientId, replyTo: 'mqtt.' + data.clientId};
        }

        requestObject = new RequestObject(data, {controller: controller, collection: collection, action: action}, 'mq');

        kuzzle.router.listener.add(requestObject, connection);
        kuzzle.funnel.execute(requestObject, connection)
          .then(function (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function (error) {
            kuzzle.emit(requestObject.controller + ':mq:funnel:reject', error);
            kuzzle.pluginsManager.trigger('log:error', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      }); // end listenExchange
    }); // end async
  };
};

/**
 * Transmit HTTP requests to the funnel controller and forward its response back to
 * the client
 *
 * @param params contains the request metadata
 * @param request is the original request from the client
 * @param response is the HTTP connection handler
 */
function executeFromRest(params, request, response) {
  var
    requestObject,
    data,
    connection = {type: 'rest', response: response};

  if (!params.controller) {
    response.writeHead(400, {'Content-Type': 'application/json'});
    response.end(stringify({error: 'The "controller" argument is missing', result: null}));
    return false;
  }

  data = {
    controller: params.controller,
    action: params.action || request.params.action,
    collection: request.params.collection
  };

  if (request.params.id) {
    data._id = request.params.id;
  }

  requestObject = new RequestObject(data, request.body, 'rest');

  response.writeHead('Access-Control-Allow-Origin', '*');
  response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');

  this.router.listener.add(requestObject, connection);

  this.funnel.execute(requestObject)
    .then(function (responseObject) {
      response.writeHead(200, {'Content-Type': 'application/json'});
      if (!_.isEmpty(responseObject)) {
        response.end(stringify(responseObject.toJson()));
      }

    })
    .catch(function (error) {
      this.emit(requestObject.controller + ':websocket:funnel:reject', error);
      this.pluginsManager.trigger('log:error', error);
      response.writeHead(400, {'Content-Type': 'application/json'});
      response.end(stringify({error: error.message, result: null}));
    }.bind(this));
}
<commit_msg>enforce the content-type of the rest document to be "application/json"<commit_after>var
  async = require('async'),
  _ = require('lodash'),
  stringify = require('json-stable-stringify'),
  Router = require('router'),
  bodyParser = require('body-parser'),
  finalhandler = require('finalhandler'),
  RequestObject = require('../core/models/requestObject'),
  ResponseListener = require('../core/responseListener');

module.exports = function RouterController (kuzzle) {
  this.router = null;
  this.controllers = ['write', 'read', 'subscribe', 'admin', 'bulk'];
  this.kuzzle = kuzzle;
  this.listener = new ResponseListener(this.kuzzle, this.kuzzle.config.queues.workerWriteResponseQueue);

  /**
   * Initializes the HTTP routes for the Kuzzle REST API.
   */
  this.initRouterHttp = function () {
    var
      api = new Router(),
      coverage;

    this.router = new Router();

    // create and mount a new router for the coverage API
    if (process.env.FEATURE_COVERAGE === '1') {
      coverage = require('istanbul-middleware');
      this.router.use('/coverage', coverage.createHandler({ resetOnGet: true }));
    }

    // create and mount a new router for our API
    this.router.use('/api/', api);

    // add a body parsing middleware to our API
    api.use(bodyParser.json());

    // Simple hello world to let know to the user that kuzzle is running
    api.get('/', function (request, response) {
      response.writeHead('Access-Control-Allow-Origin', '*');
      response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');
      response.writeHead(200, {'Content-Type': 'application/json'});
      response.end(stringify({error: null, result: 'Hello from Kuzzle :)'}));
    });

    api.post('/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'putMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.get('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'getMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_search', function (request, response) {
      var params = {
        controller: 'read',
        action: 'search'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection/_query', function (request, response) {
      var params = {
        controller: 'write',
        action: 'deleteByQuery'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection/_count', function (request, response) {
      var params = {
        controller: 'read',
        action: 'count'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/:id/_:action', function (request, response) {
      var params = {
        controller: 'write'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.put('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'createOrUpdate'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.post('/:collection', function (request, response) {
      var params = {
        controller: 'write',
        action: 'create'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.get('/:collection/:id', function (request, response) {
      var params = {
        controller: 'read',
        action: 'get'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'delete'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

    api.delete('/:collection', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'deleteCollection'
      };

      executeFromRest.call(kuzzle, params, request, response);
    });

  };

  /**
   * Forward incoming REST requests to the HTTP routes created by the
   * initRouterHttp function.
   *
   * @param request transmitted through the REST API
   * @param response is the HTTP connection handler
   */
  this.routeHttp = function (request, response) {
    kuzzle.pluginsManager.trigger('log:silly', 'Handle HTTP request');

    this.router(request, response, finalhandler(request, response));
  };

  /**
   * Handles requests coming from websocket connections
   *
   * @param {Object} socket client
   */
  this.routeWebsocket = function (socket) {
    var
      routerCtrl = this,
      connection = {type: 'websocket', id: socket.id};

    async.each(routerCtrl.controllers, function recordSocketListener (controller) {
      socket.on(controller, function (data) {
        var requestObject = new RequestObject(data, {controller: controller}, 'websocket');

        kuzzle.pluginsManager.trigger('log:silly', 'Handle Websocket for controller ' + controller);
        kuzzle.router.listener.add(requestObject, connection);

        // Execute the funnel. Forward any non-empty response to the user.
        kuzzle.funnel.execute(requestObject, connection)
          .then(function onExecuteSuccess (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function onExecuteError(error) {
            kuzzle.emit(requestObject.controller + ':websocket:funnel:reject', error);
            kuzzle.pluginsManager.trigger('log:error', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      });
    });

    // handles socket disconnections
    socket.on('disconnect', function () {
      kuzzle.pluginsManager.trigger('websocket:disconnect', 'A client is disconnected');
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });

    // handles socket crashes
    socket.on('error', function (error) {
      kuzzle.pluginsManager.trigger('websocket:error', error);
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });
  };

  /**
   * Handles requests coming from MQ protocols: AMQP, MQTT & STOMP
   *
   */
  this.routeMQListener = function () {
    async.each(this.controllers, function recordMQListener (controller) {
      kuzzle.services.list.mqBroker.listenExchange(controller+'.*.*', function handleMQMessage(msg) {
        var
          connection = null,
          data,
          requestObject,
          rawContent,
          routingArray = msg.fields.routingKey.split('.'),
          // routingArray[0] is the controller, which we already have in the 'controller' variable
          collection = routingArray[1],
          action = routingArray[2];

        if (!(msg.content instanceof Buffer)) {
          rawContent = msg.content.toString();
        }
        else {
          rawContent = (new Buffer(msg.content)).toString();
        }

        try {
          data = JSON.parse(rawContent);
        }
        catch (e) {
          kuzzle.pluginsManager.trigger('log:error', {message: 'Parse error', error: e});
          return false;
        }

        kuzzle.pluginsManager.trigger('log:silly', 'Handle MQ input' + msg.fields.routingKey);

        // For MQTT messages, we do not have a replyTo header like with AMQP or STOMP
        // => MQTT client has to send its mqtt client id and subscribe to the topic exchange mqtt.<clientId>
        //    to get feedback from Kuzzle.
        if (msg.properties && msg.properties.replyTo) {
          connection = {type: 'amq', id: data.clientId, replyTo: msg.properties.replyTo};
        }
        else {
          connection = {type: 'mqtt', id: data.clientId, replyTo: 'mqtt.' + data.clientId};
        }

        requestObject = new RequestObject(data, {controller: controller, collection: collection, action: action}, 'mq');

        kuzzle.router.listener.add(requestObject, connection);
        kuzzle.funnel.execute(requestObject, connection)
          .then(function (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function (error) {
            kuzzle.emit(requestObject.controller + ':mq:funnel:reject', error);
            kuzzle.pluginsManager.trigger('log:error', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      }); // end listenExchange
    }); // end async
  };
};

/**
 * Transmit HTTP requests to the funnel controller and forward its response back to
 * the client
 *
 * @param params contains the request metadata
 * @param request is the original request from the client
 * @param response is the HTTP connection handler
 */
function executeFromRest(params, request, response) {
  var
    requestObject,
    data,
    connection = {type: 'rest', response: response};

  if (!params.controller) {
    response.writeHead(400, {'Content-Type': 'application/json'});
    response.end(stringify({error: 'The "controller" argument is missing', result: null}));
    return false;
  }

  if (request.headers['content-type'] !== 'application/json') {
    response.writeHead(400, {'Content-Type': 'application/json'});
    response.end(stringify({error: 'Invalid request content-type. Expected "application/json", got: "' + request.headers['content-type'] +'"', result: null}));
    return false;
  }

  data = {
    controller: params.controller,
    action: params.action || request.params.action,
    collection: request.params.collection
  };

  if (request.params.id) {
    data._id = request.params.id;
  }

  requestObject = new RequestObject(data, request.body, 'rest');

  response.writeHead('Access-Control-Allow-Origin', '*');
  response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');

  this.router.listener.add(requestObject, connection);

  this.funnel.execute(requestObject)
    .then(function (responseObject) {
      response.writeHead(200, {'Content-Type': 'application/json'});
      if (!_.isEmpty(responseObject)) {
        response.end(stringify(responseObject.toJson()));
      }

    })
    .catch(function (error) {
      this.emit(requestObject.controller + ':websocket:funnel:reject', error);
      this.pluginsManager.trigger('log:error', error);
      response.writeHead(400, {'Content-Type': 'application/json'});
      response.end(stringify({error: error.message, result: null}));
    }.bind(this));
}
<|endoftext|>


<commit_before>/**
 * MegaTable
 * Load infinite sized tables in the browser with a fixed number of elements
 * @constructor
 */
var MegaTable = (function(document) {

	/**
	 *
	 * @param {Object} settings inherits values from MegaTable.defaultSettings
	 * @constructor
	 */
	function MegaTable(settings) {

		var table = this.table = document.createElement('table'),
			colGroup = this.colGroup = document.createElement('colGroup'),
			tBody = this.tBody = document.createElement('tBody'),
			defaults = MegaTable.defaultSettings,
			i;

		for(i in defaults) if (defaults.hasOwnProperty(i)) {
			if (settings[i] === undefined) settings[i] = defaults[i];
		}

		this.rows = settings.rows;
		this.columns = settings.columns;
		this.updateCell = settings.updateCell;
		this.updateRowHeader = settings.updateRowHeader;
		this.updateColumnHeader = settings.updateColumnHeader;
		this.rowIndex = 0;
		this.columnIndex = 0;

		table.appendChild(colGroup);
		table.appendChild(tBody);

		if (settings.strict) {
			table.style.tableLayout = 'fixed';
			table.style.width = '0px';
		}
		table.className = 'mega-table';

		this._createMegaTableDOM();

		if (settings.element !== null) {
			settings.element.appendChild(table);
		}
	}

	MegaTable.prototype = {
		/**
		 *
		 * @param {Number} rowIndex
		 * @param {Number} columnIndex
		 */
		update: function (rowIndex, columnIndex) {
			this.updateRows(rowIndex);
			this.updateColumns(columnIndex);
		},

		/**
		 * @param {Number} rowIndex
		 */
		updateRows: function (rowIndex) {
			var up,
				down,
				detachedRow;

			if (this.rowIndex > rowIndex) {
				up = true;
				down = false;
				this.rowIndex = rowIndex;
			} else if (this.rowIndex < rowIndex) {
				down = true;
				up = false;
				this.rowIndex = rowIndex;
			}

			detachedRow = this._updateRowHeaders(up, down);
			this._updateBody(detachedRow, up, down);
		},

		/**
		 *
		 * @param {Number} columnIndex
		 */
		updateColumns: function (columnIndex) {
			var left,
				right;

			if (this.columnIndex > columnIndex) {
				left = true;
				right = false;
				this.columnIndex = columnIndex;
			} else if (this.columnIndex < columnIndex) {
				right = true;
				left = false;
				this.columnIndex = columnIndex;
			}

			this._updateColumnHeaders(left, right);
			this._updateBody(null, false, false, left, right);
		},

		//used in instantiation
		_createCornerDOM: function (tr) {
			var th = document.createElement('th'),
				col = document.createElement('col');

			tr.appendChild(th);
			this.colGroup.appendChild(col);
		},

		_createColumnHeaderDOM: function () {
			var tr = document.createElement('tr'),
				colGroup = this.colGroup,
				columnIndex = this.columnIndex,
				columns = this.columns,
				i = 0,
				th,
				col;

			this._createCornerDOM(tr);

			this.tBody.appendChild(tr);

			for (; i <= columns; i++) {
				th = document.createElement('th');
				tr.appendChild(th);
				col = document.createElement('col');
				colGroup.appendChild(col);

				this.updateColumnHeader(columnIndex + i, th, col);
			}
		},

		_createRowHeaderDOM: function (tr, i) {
			var th = document.createElement('th');
			this.updateRowHeader(i, th);
			tr.appendChild(th);
		},

		_createMegaTableDOM: function () {
			var tBody = this.tBody,
				max = this.rows,
				rowIndex = this.rowIndex,
				columns = this.columns,
				i = 0,
				columnIndex = 0,
				tr,
				td;

			this._createColumnHeaderDOM();

			for (; i <= max; i++) {
				columnIndex = 0;
				tr = document.createElement('tr');

				this._createRowHeaderDOM(tr, rowIndex + i);

				tBody.appendChild(tr);
				for (; columnIndex <= columns; columnIndex++) {
					td = document.createElement('td');
					tr.appendChild(td);
					this.updateCell(rowIndex, columnIndex, td);
				}
			}
		},

		//used in updating
		_updateColumnHeaders: function (left, right) {
			var parent = this.tBody.children[0],
				colGroup = this.colGroup,
				col,
				header;

			if (left) {
				header = parent.lastChild;
				col = colGroup.lastChild;

				parent.removeChild(header);
				colGroup.removeChild(col);

				header.removeAttribute('style');
				header.removeAttribute('class');
				col.removeAttribute('style');

				this.updateColumnHeader(this.columnIndex, header, col);

				//insert before corner
				parent.insertBefore(header, parent.children[0]);
				colGroup.insertBefore(col, colGroup.children[0]);
			} else if (right) {
				header = parent.children[1];
				col = colGroup.children[1];

				parent.removeChild(header);
				colGroup.removeChild(col);

				header.removeAttribute('style');
				header.removeAttribute('class');
				col.removeAttribute('style');

				this.updateColumnHeader(this.columnIndex + parent.children.length, header, col);

				//insert at end
				parent.appendChild(header);
				colGroup.appendChild(col);
			}
		},

		_updateRowHeaders: function (up, down) {
			var parent = this.tBody,
				header;

			if (up) {
				header = parent.lastChild.children[0];

				//we intentionally leave the node detached here because the body manages it
				parent.removeChild(header.parentNode);

				header.removeAttribute('style');
				header.removeAttribute('class');

				this.updateRowHeader(this.rowIndex, header);

				return header.parentNode;
			} else if (down) {
				header = parent.children[1].children[0];

				//we intentionally leave the node detached here because the body manages it
				parent.removeChild(header.parentNode);

				header.removeAttribute('style');
				header.removeAttribute('class');

				this.updateRowHeader(this.rowIndex + parent.children.length, header);

				return header.parentNode;
			}
			return null;
		},

		_moveBottomRowToTop: function (row) {
			var children = row.children,
				max = children.length,
				element,
				i;

			for (i = 1; i < max; i++) {
				element = children[i];

				element.attributes['style'] =
					element.className = '';

				if (element.rowSpan > 0) {
					element.rowSpan = 0;
				}

				if (element.colSpan > 0) {
					element.colSpan = 0;
				}

				this.updateCell(this.rowIndex, i + this.columnIndex, element);
			}

			this.tBody.insertBefore(row, this.tBody.children[1]);
		},

		_moveTopRowToBottom: function (row) {
			var children = row.children,
				max = children.length,
				element,
				i;

			for (i = 1; i < max; i++) {
				element = children[i];

				element.removeAttribute('style');
				element.removeAttribute('colSpan');
				element.removeAttribute('rowSpan');
				element.removeAttribute('class');

				this.updateCell(this.rowIndex + this.tBody.children.length, i + this.columnIndex, element);
			}

			this.tBody.insertBefore(row, null);
		},

		_moveRightColumnToLeft: function () {
			this.table.style.visibility = 'hidden';

			var rows = this.tBody.children,
				max = rows.length,
				row,
				tdElement,
				i;

			for (i = 1; i < max; i++) {
				row = rows[i];
				tdElement = row.lastChild;
				row.removeChild(tdElement);

				this.updateCell(this.rowIndex + i, this.columnIndex, tdElement);

				row.insertBefore(tdElement, row.children[1]);
			}

			this.table.style.visibility = '';
		},

		_moveLeftColumnToRight: function () {
			this.table.style.visibility = 'hidden';

			var rows = this.tBody.children,
				max = rows.length - 1,
				row,
				columnIndexEnd = this.columnIndex + rows[0].children.length,
				tdElement,
				i;

			for (i = 1; i < max; i++) {
				row = rows[i];
				tdElement = row.children[1];
				row.removeChild(tdElement);

				this.updateCell(this.rowIndex + i, columnIndexEnd, tdElement);

				row.insertBefore(tdElement, null);
			}

			this.table.style.visibility = '';
		},

		_updateBody: function (row, up, down, left, right) {
			if (up) {
				this._moveBottomRowToTop(row);
			} else if (down) {
				this._moveTopRowToBottom(row);
			} else if (left) {
				this._moveRightColumnToLeft();
			} else if (right) {
				this._moveLeftColumnToRight();
			}
		}
	};

	/**
	 *
	 * @type {Object}
	 */
	MegaTable.defaultSettings = {
		/**
		 * element you want mega table in
		 * @type {Element}
		 */
		element: null,

		/**
		 * number of rows you'd like your table to have
		 * @type {Number}
		 */
		rows: 10,

		/**
		 * number of columns you'd like your table to have
		 * @type {Number}
		 */
		columns: 10,

		/**
		 * callback for when a table data element needs updated
		 * @type {Function}
		 * @param {Number} row
		 * @param {Number} column
		 * @param {HTMLTableCellElement} td
		 */
		updateCell: function(row, column, td) {},

		/**
		 * callback for when a table row header element needs updated
		 * @type {Function}
		 * @param {Number} i
		 * @param {HTMLTableCellElement} th
		 */
		updateRowHeader: function(i, th) {},

		/**
		 * callback for when a table column header element needs updated
		 * @type {Function}
		 * @param {Number} i
		 * @param {HTMLTableCellElement} th
		 * @param {HTMLTableColElement} col
		 */
		updateColumnHeader: function(i, th, col) {},

		/**
		 * turns on strict mode so that size comes strictly from col elements
		 * @type {Boolean}
		 */
		strict: false
	};

	return MegaTable;
})(document);<commit_msg>Performance fixes and scrolling up and left fixes Added corner size update support<commit_after>/**
 * MegaTable
 * Load infinite sized tables in the browser with a fixed number of elements
 * @constructor
 */
var MegaTable = (function(document) {
	"use strict";

	var charSize = function() {
		var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
			el = document.createElement('span'),
			size;

		el.innerHTML = characters;
		document.body.appendChild(el);

		size = {
			width: el.offsetWidth / characters.length,
			height: el.offsetHeight
		};

		el.parentNode.removeChild(el);

		return size;
	};

	/**
	 *
	 * @param {Object} settings inherits values from MegaTable.defaultSettings
	 * @constructor
	 */
	function MegaTable(settings) {

		var table = this.table = document.createElement('table'),
			colGroup = this.colGroup = document.createElement('colGroup'),
			tBody = this.tBody = document.createElement('tBody'),
			defaults = MegaTable.defaultSettings,
			i;

		this.charSize = charSize();

		for(i in defaults) if (defaults.hasOwnProperty(i)) {
			if (settings[i] === undefined) settings[i] = defaults[i];
		}

		this.rows = settings.rows;
		this.columns = settings.columns;
		this.updateCell = settings.updateCell;
		this.updateRowHeader = settings.updateRowHeader;
		this.updateColumnHeader = settings.updateColumnHeader;
		this.rowIndex = 0;
		this.columnIndex = 0;

		table.appendChild(colGroup);
		table.appendChild(tBody);

		if (settings.strict) {
			table.style.tableLayout = 'fixed';
			table.style.width = '0px';
		}
		table.className = 'mega-table';

		this._createMegaTableDOM();

		if (settings.element !== null) {
			settings.element.appendChild(table);
		}
	}

	MegaTable.prototype = {
		/**
		 *
		 * @param {Number} rowIndex
		 * @param {Number} columnIndex
		 */
		update: function (rowIndex, columnIndex) {
			this.updateRows(rowIndex);
			this.updateColumns(columnIndex);
		},

		/**
		 * @param {Number} rowIndex
		 */
		updateRows: function (rowIndex) {
			var up = 0,
				down = 0,
				detachedRow;

			if (this.rowIndex > rowIndex) {
				up = this.rowIndex - rowIndex;
			} else if (this.rowIndex < rowIndex) {
				down = rowIndex - this.rowIndex;
			}

			if (up > 0) {
				while (up > 0) {
					this.rowIndex--;
					detachedRow = this._moveBottomRowHeaderToTop();
					this._moveBottomRowToTop(detachedRow);
					up--;
				}
				this._updateCorner();
			}

			else if (down > 0) {
				while (down > 0) {
					this.rowIndex++;
					detachedRow = this._moveTopRowHeaderToBottom();
					this._moveTopRowToBottom(detachedRow);
					down--;
				}
				this._updateCorner();
			}
		},

		/**
		 *
		 * @param {Number} columnIndex
		 */
		updateColumns: function (columnIndex) {
			var left = 0,
				right = 0;

			if (this.columnIndex > columnIndex) {
				left = this.columnIndex - columnIndex;
			} else if (this.columnIndex < columnIndex) {
				right = columnIndex - this.columnIndex;
			}

			if (left > 0) {
				this.table.style.visibility = 'hidden';
				while (left > 0) {
					this.columnIndex--;
					this.moveRightColumnHeaderToLeft();
					this._moveRightColumnToLeft();
					left--;
				}
				this.table.style.visibility = '';
			}

			else if (right > 0) {
				this.table.style.visibility = 'hidden';
				while (right > 0) {
					this.columnIndex++;
					this._moveLeftColumnHeaderToRight();
					this._moveLeftColumnToRight();
					right--;
				}
				this.table.style.visibility = '';
			}
		},

		//used in instantiation
		_createCornerDOM: function (tr) {
			var th = document.createElement('th'),
				col = this.cornerCol = document.createElement('col');

			col.style.width = '14px';

			tr.appendChild(th);
			this.colGroup.appendChild(col);
		},

		_createColumnHeaderDOM: function () {
			var tr = document.createElement('tr'),
				colGroup = this.colGroup,
				columnIndex = this.columnIndex,
				columns = this.columns,
				i = 0,
				th,
				col;

			this._createCornerDOM(tr);

			this.tBody.appendChild(tr);

			for (; i <= columns; i++) {
				th = document.createElement('th');
				tr.appendChild(th);
				col = document.createElement('col');
				colGroup.appendChild(col);

				this.updateColumnHeader(columnIndex + i, th, col);
			}
		},

		_createRowHeaderDOM: function (tr, i) {
			var th = document.createElement('th');
			this.updateRowHeader(i, th);
			tr.appendChild(th);
		},

		_createMegaTableDOM: function () {
			var tBody = this.tBody,
				max = this.rows,
				rowIndex = this.rowIndex,
				columns = this.columns,
				i = 0,
				columnIndex = 0,
				tr,
				td;

			this._createColumnHeaderDOM();

			for (; i <= max; i++) {
				columnIndex = 0;
				tr = document.createElement('tr');

				this._createRowHeaderDOM(tr, rowIndex + i);

				tBody.appendChild(tr);
				for (; columnIndex <= columns; columnIndex++) {
					td = document.createElement('td');
					tr.appendChild(td);
					this.updateCell(rowIndex + i, columnIndex, td);
				}
			}
		},

		//used in updating
		_updateCorner: function() {
			var tBody = this.tBody,
				col = this.cornerCol,
				targetRow = tBody.lastChild,
				th = targetRow.firstChild,
				newWidth,
				minWidth = 20;

			newWidth = this.charSize.width * (th.textContent || th.innerText).length;
			//set a minimum width, because css doesn't respect this on col in FF
			newWidth = (newWidth > minWidth ? newWidth : minWidth);

			if (newWidth !== col._width || col._width === undefined) {
				col._width = newWidth;
				col.style.width = newWidth + 'px';
			}
		},
		moveRightColumnHeaderToLeft: function() {
			var parent = this.tBody.children[0],
				colGroup = this.colGroup,
				col = colGroup.lastChild,
				header = parent.lastChild;

			parent.removeChild(header);
			colGroup.removeChild(col);

			while(header.lastChild !== null) {
				header.removeChild(header.lastChild);
			}
			header.removeAttribute('style');
			header.removeAttribute('class');
			col.removeAttribute('style');

			this.updateColumnHeader(this.columnIndex, header, col);

			//insert before corner
			parent.insertBefore(header, parent.children[1]);
			colGroup.insertBefore(col, colGroup.children[1]);
		},
		_moveLeftColumnHeaderToRight: function() {
			var parent = this.tBody.children[0],
				colGroup = this.colGroup,
				col = colGroup.children[1],
				header = parent.children[1];

			parent.removeChild(header);
			colGroup.removeChild(col);

			while(header.lastChild !== null) {
				header.removeChild(header.lastChild);
			}
			header.removeAttribute('style');
			header.removeAttribute('class');
			col.removeAttribute('style');

			this.updateColumnHeader(this.columnIndex + parent.children.length, header, col);

			//insert at end
			parent.appendChild(header);
			colGroup.appendChild(col);
		},

		_moveBottomRowHeaderToTop: function() {
			var parent = this.tBody,
				header = parent.lastChild.children[0];

			//we intentionally leave the node detached here because the body manages it
			parent.removeChild(header.parentNode);

			while(header.lastChild !== null) {
				header.removeChild(header.lastChild);
			}
			header.removeAttribute('style');
			header.removeAttribute('class');

			this.updateRowHeader(this.rowIndex, header);

			return header.parentNode;
		},
		_moveTopRowHeaderToBottom: function() {
			var parent = this.tBody,
				header = parent.children[1].children[0];

			//we intentionally leave the node detached here because the body manages it
			parent.removeChild(header.parentNode);

			while(header.lastChild !== null) {
				header.removeChild(header.lastChild);
			}
			header.removeAttribute('style');
			header.removeAttribute('class');

			this.updateRowHeader(this.rowIndex + parent.children.length, header);

			return header.parentNode;
		},

		_moveBottomRowToTop: function (row) {
			var children = row.children,
				max = children.length,
				element,
				i;

			for (i = 1; i < max; i++) {
				element = children[i];

				while(element.lastChild !== null) {
					element.removeChild(element.lastChild);
				}
				element.removeAttribute('style');
				element.removeAttribute('colSpan');
				element.removeAttribute('rowSpan');
				element.removeAttribute('class');

				this.updateCell(this.rowIndex, this.columnIndex + i - 1, element);
			}

			this.tBody.insertBefore(row, this.tBody.children[1]);
		},

		_moveTopRowToBottom: function (row) {
			var children = row.children,
				max = children.length,
				element,
				i;

			for (i = 1; i < max; i++) {
				element = children[i];

				while(element.lastChild !== null) {
					element.removeChild(element.lastChild);
				}
				element.removeAttribute('style');
				element.removeAttribute('colSpan');
				element.removeAttribute('rowSpan');
				element.removeAttribute('class');

				this.updateCell(this.rowIndex + this.tBody.children.length, this.columnIndex + i - 1, element);
			}

			this.tBody.insertBefore(row, null);
		},

		_moveRightColumnToLeft: function () {
			var rows = this.tBody.children,
				max = rows.length,
				row,
				tdElement,
				i;

			for (i = 1; i < max; i++) {
				row = rows[i];
				tdElement = row.lastChild;
				row.removeChild(tdElement);

				while(tdElement.lastChild !== null) {
					tdElement.removeChild(tdElement.lastChild);
				}

				this.updateCell(this.rowIndex + i - 1, this.columnIndex, tdElement);

				row.insertBefore(tdElement, row.children[1]);
			}
		},

		_moveLeftColumnToRight: function () {
			var rows = this.tBody.children,
				max = rows.length - 1,
				row,
				columnIndexEnd = this.columnIndex + rows[0].children.length,
				tdElement,
				i;

			for (i = 1; i < max; i++) {
				row = rows[i];
				tdElement = row.children[1];
				row.removeChild(tdElement);

				while(tdElement.lastChild !== null) {
					tdElement.removeChild(tdElement.lastChild);
				}

				this.updateCell(this.rowIndex + i - 1, columnIndexEnd, tdElement);

				row.insertBefore(tdElement, null);
			}
		}
	};

	/**
	 *
	 * @type {Object}
	 */
	MegaTable.defaultSettings = {
		/**
		 * element you want mega table in
		 * @type {Element}
		 */
		element: null,

		/**
		 * number of rows you'd like your table to have
		 * @type {Number}
		 */
		rows: 10,

		/**
		 * number of columns you'd like your table to have
		 * @type {Number}
		 */
		columns: 10,

		/**
		 * callback for when a table data element needs updated
		 * @type {Function}
		 * @param {Number} row
		 * @param {Number} column
		 * @param {HTMLTableCellElement} td
		 */
		updateCell: function(row, column, td) {},

		/**
		 * callback for when a table row header element needs updated
		 * @type {Function}
		 * @param {Number} i
		 * @param {HTMLTableCellElement} th
		 */
		updateRowHeader: function(i, th) {},

		/**
		 * callback for when a table column header element needs updated
		 * @type {Function}
		 * @param {Number} i
		 * @param {HTMLTableCellElement} th
		 * @param {HTMLTableColElement} col
		 */
		updateColumnHeader: function(i, th, col) {},

		/**
		 * turns on strict mode so that size comes strictly from col elements
		 * @type {Boolean}
		 */
		strict: false
	};

	return MegaTable;
})(document);<|endoftext|>


<commit_before>
var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000)<commit_msg>added ;<commit_after>
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World');
});

app.listen(3000);<|endoftext|>