<commit_before>// See: https://gist.github.com/xiaodaigh/6445698
// TODO Change to more selective solution
var swichTab = function(i) {
    tabs = $('.tabbable .nav.nav-tabs li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $('.tabbable .tab-content .tab-pane')
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left'
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'bottom left',
        at: 'top center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
Set parameters...
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
... and start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab(2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    },  {
        content: [
            '
... and will be available for download/p>'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#paea_downloads'),
        my: 'top left',
        at: 'top left'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    window.setTimeout(initTour, 1000);
})<commit_msg>Make tab switching more selective to work with multiple tabpanels<commit_after>// See: https://gist.github.com/xiaodaigh/6445698
var swichTab = function(tabId, i) {
    tabs = $(tabId).find('li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $(tabId).next('.tab-content').children('.tab-pane');
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left'
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'bottom left',
        at: 'top center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
Set parameters...
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'bottom center'
    }, {
        content: [
            '
... and start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    },  {
        content: [
            '
... and will be available for download/p>'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#paea_downloads'),
        my: 'top left',
        at: 'top left'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    window.setTimeout(initTour, 1000);
})<|endoftext|>


<commit_before>import createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {composeWithDevTools} from 'redux-devtools-extension';
import {queryReducer} from './reducers';

const middlewares = [thunk];
const store = createStore(queryReducer, composeWithDevTools(applyMiddleware(...middlewares)));

export default store;
<commit_msg>whoops<commit_after>import createStore, {applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {composeWithDevTools} from 'redux-devtools-extension';
import {queryReducer} from './reducers';

const middlewares = [thunk];
const store = createStore(queryReducer, composeWithDevTools(applyMiddleware(...middlewares)));

export default store;
<|endoftext|>


<commit_before>'use strict';

angular.module('hackatonAApp')
    .controller('MainCtrl', function ($scope, $resource) {
        $scope.awesomeThings = [
            'HTML5 Boilerplate',
            'AngularJS',
            'Karma'
        ];

        var Stats = $resource(" /api/EventStats?event_id=:eventId", {eventId:"@id"});
        var ListProj=$resource("/api/project?name=:name&event_id=:eventId",{name:"@name",eventId:"@id"});

        var dates;
        var counts_commit;
        var stat=Stats.get({eventId:1},function(){
            $scope.projects_count=stat.projects_count;
            $scope.members_count=stat.members_count;
            $scope.commits_count=stat.commits_count;

            dates=stat.commit_detail.dates;
            counts_commit=stat.commit_detail.counts;

            var lineChartData = {
                labels: dates,
                datasets: [
                    {
                        fillColor: "rgba(79,206,174,0.1)",
                        strokeColor: "rgba(79,206,174,1)",
                        pointColor: "rgba(79,206,174,1)",
                        pointStrokeColor: "#f2f2f3",
                        data: counts_commit
                    }
                ]

            }

            var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(lineChartData);
        });

        $scope.setMedal=function(index, mark){
            if(mark>0){
                switch (index){
                    case 0:
                        return "b-item__medal_gold";
                        break;

                    case 1:
                        return "b-item__medal_silver";
                        break;

                    case 2:
                        return "b-item__medal_bronze";
                }
            }
        }

    });
<commit_msg>fixed<commit_after>'use strict';

angular.module('hackatonAApp')
    .controller('MainCtrl', function ($scope, $resource) {
        $scope.awesomeThings = [
            'HTML5 Boilerplate',
            'AngularJS',
            'Karma'
        ];

        var Stats = $resource(" /api/EventStats?event_id=:eventId", {eventId:"@id"});
        var ListProj=$resource("/api/project?name=:name&event_id=:eventId",{name:"@name",eventId:"@id"});

        var dates;
        var counts_commit;
        var stat=Stats.get({eventId:1},function(){
            $scope.projects_count=stat.projects_count;
            $scope.members_count=stat.members_count;
            $scope.commits_count=stat.commits_count;

            dates=stat.commit_detail.dates;
            counts_commit=stat.commit_detail.counts;

            var lineChartData = {
                labels: dates,
                datasets: [
                    {
                        fillColor: "rgba(79,206,174,0.1)",
                        strokeColor: "rgba(79,206,174,1)",
                        pointColor: "rgba(79,206,174,1)",
                        pointStrokeColor: "#f2f2f3",
                        data: counts_commit
                    }
                ]

            }

            var myLine = new Chart(document.getElementById("canvas").getContext("2d")).Line(lineChartData);
        });

        $scope.listProj=ListProj.query();

        $scope.setMedal=function(index, mark){
            if(mark>0){
                switch (index){
                    case 0:
                        return "b-item__medal_gold";
                        break;

                    case 1:
                        return "b-item__medal_silver";
                        break;

                    case 2:
                        return "b-item__medal_bronze";
                }
            }
        }

    });
<|endoftext|>


<commit_before>#ifndef __lang_Json__
#define __lang_Json__

////////////////////////////////////////////////////////////////////////////////
// Json

Json = new function() {
#ifndef __RHINO // TODO: see bellow
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' };

	function replace(chr) {
		return special[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	}
#endif // !RHINO

	return {
		encode: function(obj) {
#ifdef __RHINO // TODO: uneval seems to cause problems with empty fields in arrays in Rhino (resulting in [,,,])
			var str = uneval(obj);
			return str[0] == '(' ? str.substring(1, str.length - 1) : str;
#else // !RHINO
			switch (Base.type(obj)) {
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, replace) + '"';
				case 'array':
					return '[' + obj.map(this.encode).compact().join(',') + ']';
				case 'object':
					return '{' + Base.each(obj, function(val, key) {
						if (val != undefined)
							this.push(Json.encode(key) + ':' + Json.encode(val));
					}, []) + '}';
				/*
					return '{' + Hash.map(obj, function(val, key) {
						val = Json.encode(val);
						if (val) return Json.encode(key) + ':' + val;
					}).compact() + '}';
				*/
				default:
					return obj + '';
			}
			return null;
#endif // !RHINO
		},

		decode: function(string, secure) {
			try {
				return (Base.type(string) != 'string' || !string.length) ||
					(secure && !/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(
						string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '')))
					? null : eval('(' + string + ')');
			} catch (e) {
				return null;
			}
		}
	};
};

#endif // __lang_Json__<commit_msg>Changed to use #collect.<commit_after>#ifndef __lang_Json__
#define __lang_Json__

////////////////////////////////////////////////////////////////////////////////
// Json

Json = new function() {
#ifndef __RHINO // TODO: see bellow
	var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' };

	function replace(chr) {
		return special[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	}
#endif // !RHINO

	return {
		encode: function(obj) {
#ifdef __RHINO // TODO: uneval seems to cause problems with empty fields in arrays in Rhino (resulting in [,,,])
			var str = uneval(obj);
			return str[0] == '(' ? str.substring(1, str.length - 1) : str;
#else // !RHINO
			switch (Base.type(obj)) {
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, replace) + '"';
				case 'array':
					return '[' + obj.collect(Json.encode) + ']';
				case 'hash':
				case 'object':
					return '{' + Hash.collect(obj, function(val, key) {
						val = Json.encode(val);
						if (val) return Json.encode(key) + ':' + val;
					}) + '}';
				default:
					return obj + '';
			}
			return null;
#endif // !RHINO
		},

		decode: function(string, secure) {
			try {
				return (Base.type(string) != 'string' || !string.length) ||
					(secure && !/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/.test(
						string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '')))
					? null : eval('(' + string + ')');
			} catch (e) {
				return null;
			}
		}
	};
};

#endif // __lang_Json__<|endoftext|>


<commit_before>/**
 * mobileapp provides page content for the Mobile Apps.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easy to consume by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var sUtil = require('../lib/util');

// shortcut
var HTTPError = sUtil.HTTPError;

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

// gallery constants:
var MAX_ITEM_COUNT = 256;
var MIN_IMAGE_SIZE = 64;
var MAX_IMAGE_WIDTH = 1280;

function dbg(name, obj) {
    //console.log("DEBUG: " + name + ": " + JSON.stringify(obj, null, 2));
    app.logger.log('debug', name + ": " + JSON.stringify(obj));
}

/**
 * A helper function that obtains the HTML from the MW API and
 * loads it into a domino DOM document instance.
 *
 * @param {String} domain the domain to contact
 * @param {Object} params an Object with all the query parameters for the MW API
 * @return {Promise} a promise resolving as the HTML element object
 */
function apiGet(domain, params) {
    // get the page from the MW API
    return preq.get({
        uri: 'http://' + domain + '/w/api.php',
        query: params
    });
}

function rmSelectorAll(doc, selector) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.parentNode.removeChild(node);
    }
}

function rmBracketSpans(doc) {
    var ps = doc.querySelectorAll('span:not([class],[style],[id])') || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        if (node.innerHTML === '[') {
            var leftBracket = doc.createTextNode('[');
            node.parentNode.replaceChild(leftBracket, node);
        } else if (node.innerHTML === ']') {
            var rightBracket = doc.createTextNode(']');
            node.parentNode.replaceChild(rightBracket, node);
        }
    }
}

function rmAttributeAll(doc, selector, attribute) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.removeAttribute(attribute);
    }
}

function moveFirstParagraphUpInLeadSection(text) {
    var doc = domino.createDocument(text);
    // TODO: mhurd: feel free to add your lead section magic here
    return doc.body.innerHTML;
}

/**
 * Nukes stuff from the DOM we don't want.
 */
function runDomTransforms(text, sectionIndex) {
    var doc = domino.createDocument(text);

    var rmSelectors = [
        'div.noprint',
        'div.infobox',
        'div.metadata',
        'table.navbox',
        'div.magnify',
        'span[style*="display:none"]',             // Remove
        'span.Z3988'                               // Remove
    ];
    if (sectionIndex === 0) {
        rmSelectors.push('div.hatnote');
    }
    rmSelectorAll(doc, rmSelectors.join(', '));    // Do single call to rmSelectorAll.

    rmAttributeAll(doc, 'a', 'rel');
    rmAttributeAll(doc, 'a,span', 'title');
    rmAttributeAll(doc, 'img', 'alt');

    rmBracketSpans(doc);

    // TODO: mhurd: add more references to functions where you do more transforms here

    return doc.body.innerHTML;
}

function checkApiResponse(response) {
    // check if the query failed
    if (response.status > 299) {
        // there was an error in the MW API, propagate that
        throw new HTTPError({
            status: response.status,
            type: 'api_error',
            title: 'MW API error',
            detail: response.body
        });
    }
}

function checkForQueryPagesIn(response) {
    if (!response.body.query || !response.body.query.pages) {
        // we did not get our expected query.pages from the MW API, propagate that
        app.logger.log('error', 'no query.pages in response: ' + JSON.stringify(response, null, 2));
        throw new HTTPError({
            status: response.status,
            type: 'api_error',
            title: 'no query.pages in response',
            detail: response.body
        });
    }
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageContentPromise(domain, title) {
    return apiGet(domain, {
        "action": "mobileview",
        "format": "json",
        "page": title,
        "prop": "text|sections|thumb|image|id|revision|description|lastmodified|normalizedtitle|displaytitle|protection|editable",
        "sections": "all",
        "sectionprop": "toclevel|line|anchor",
        "noheadings": true
    }).then(function (response) {
        checkApiResponse(response);

        // transform all sections
        var sections = response.body.mobileview.sections;
        for (var idx = 0; idx < sections.length; idx++) {
            var section = sections[idx];
            section.text = runDomTransforms(section.text, idx);
        }

        if (!response.body.mobileview.mainpage) {
            // don't do anything if this is the main page, since many wikis
            // arrange the main page in a series of tables.
            // TODO: should we also exclude file and other special pages?
            sections[0].text = moveFirstParagraphUpInLeadSection(sections[0].text);
        }

        return response.body.mobileview;
    });
}

// in the case of video, look for a list of transcodings, so that we might
// find a WebM version, which is playable in Android.
function getTranscodedVideoUrl(objinfo) {
    var derivativesArr, derivative, url, key;
    if (objinfo.derivatives) {
        derivativesArr = objinfo.derivatives;
        for (key in derivativesArr) {
            if (derivativesArr.hasOwnProperty(key)) {
                derivative = derivativesArr[key];
                if (derivative.type && derivative.type.indexOf("webm") > -1) {
                    // that's the one!
                    url = derivative.src;
                    // Note: currently picks the last one
                    // TODO: in the future we could have an extra URL that provides a size parameter for images and videos
                }
            }
        }
    }
    return url;
}

function getExtMetadata(extmetadata) {
    var ext = {};
    for (var key in extmetadata) {
        if (extmetadata.hasOwnProperty(key)) {
            var value = extmetadata[key].value;
            if (typeof value === "string") {
                ext[key] = value.trim();
            }
        }
    }
    return ext;
}

function handleGalleryItems(item) {
    var obj, url;

    if (item.imageinfo) {
        obj = item.imageinfo[0];
    } else if (item.videoinfo) {
        obj = item.videoinfo[0];
        url = getTranscodedVideoUrl(obj);
    }
    if (!url) {
        url = obj.url;
    }
    return {
        title: item.title,
        url: url,
        thumbUrl: obj.thumbUrl,
        mimeType: obj.mimeType,
        width: obj.width,
        height: obj.height,
        ext: getExtMetadata(obj.extmetadata)
    };
}

function onGalleryItemsResponse(response) {
    var items, key;
    var output = [];

    checkApiResponse(response);
    checkForQueryPagesIn(response);

    items = response.body.query.pages;
    for (key in items) {
        if (items.hasOwnProperty(key)) {
            output.push(handleGalleryItems(items[key]));
        }
    }

    return output;
}

/** Returns a promise to retrieve one or more gallery items. */
function galleryItemsPromise(domain, titles, params) {
    Object.assign(params, {
        "action": "query",
        "format": "json",
        "titles": titles,
        "continue": ""
    });

    return apiGet(domain, params)
        .then(function (response) {
            return onGalleryItemsResponse(response);
        });
}

function onGalleryCollectionsResponse(response, domain) {
    var detailsPromises = [], videos = [], images = [];
    var isVideo;

    checkApiResponse(response);
    if (!response.body.query || !response.body.query.pages) {
        return [];
    }

    // iterate over all items
    var items = response.body.query.pages;
    for (var key in items) {
        if (items.hasOwnProperty(key)) {
            var item = items[key];

            // remove the ones that are too small or are of the wrong type
            var imageinfo = item.imageinfo[0];  // TODO: why this is an array?

            // Reject gallery items if they're too small.
            // Also reject SVG and PNG items by default, because they're likely to be
            // logos and/or presentational images.
            if (imageinfo.width < MIN_IMAGE_SIZE
                || imageinfo.height < MIN_IMAGE_SIZE
                || imageinfo.mime.indexOf("svg") > -1
                || imageinfo.mime.indexOf("png") > -1
            ) {
                delete items[key];
            } else {
                delete item.ns;
                delete item.imagerepository; // we probably don't care where the repo is
                delete imageinfo.size;
                // TODO instead of deleting properties we probably want to just add well-known properties

                isVideo = imageinfo.mime.indexOf("ogg") > -1 || imageinfo.mime.indexOf("video") > -1;

                // request details individually, to keep the order
                //detailsPromises.push(galleryItemsPromise(domain, item.title, isVideo));

                if (isVideo) {
                    videos.push(item.title);
                } else {
                    images.push(item.title);
                }
            }
        }
    }

    // one more request for all the videos
    if (videos.length > 0) {
        detailsPromises.videos = galleryItemsPromise(domain, videos.join('|'), {
            "prop": "videoinfo",
            "viprop": "url|dimensions|mime|extmetadata|derivatives",
            "viurlwidth": MAX_IMAGE_WIDTH,
        });
    }

    // another one request for all the images
    if (images.length > 0) {
        detailsPromises.images = galleryItemsPromise(domain, images.join('|'), {
            "prop": "imageinfo",
            "iiprop": "url|dimensions|mime|extmetadata",
            "iiurlwidth": MAX_IMAGE_WIDTH
        });
    }

    return detailsPromises;
}

/** Gets the gallery content from MW API */
function galleryCollectionPromise(domain, title) {
    return apiGet(domain, {
        "action": "query",
        "format": "json",
        "titles": title,
        "continue": "",
        "prop": "imageinfo",
        "iiprop": "dimensions|mime",
        "generator": "images",
        "gimlimit": MAX_ITEM_COUNT
    }).then(function (response) {
        var detailsPromises = onGalleryCollectionsResponse(response, domain);
        return BBPromise.props({
            videos: detailsPromises.videos,
            images: detailsPromises.images
        });
    });
}

/**
 * GET {domain}/v1/mobile/app/main/html/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/html/:title', function (req, res) {
    BBPromise.props({
        page: pageContentPromise(req.params.domain, req.params.title),
        media: galleryCollectionPromise(req.params.domain, req.params.title)
    }).then(function (response) {
        res.status(200).type('json').end(JSON.stringify(response));
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/mobile/app/main',
        api_version: 1,
        router: router
    };
};

<commit_msg> Change Content-Type to HTML<commit_after>/**
 * mobileapp provides facades for Mobile Apps (Android, iOS)
 * Initially, this focuses on page (view) content.
 * The goal is to avoid having to use a web view and style the content natively inside the app
 * using plain TextViews.
 * The payload should not have any extra data, and should be easily consumed by the apps.
 *
 * Status: Prototype -- not ready for production
 * Currently using the mobileview action MW API, and removing some data we don't display.
 * The output is in HTML, with two script blocks embedded for the JSON metadata:
 * one at the beginning for things the app needs for initial display and another
 * one at the end for things that could be used later (gallery).
 * TODO: add some transformations that currently are being done by the apps and remove some more unneeded data
 */

'use strict';

var BBPromise = require('bluebird');
var preq = require('preq');
var domino = require('domino');
var sUtil = require('../lib/util');
var util = require('util');

// shortcut
var HTTPError = sUtil.HTTPError;

/**
 * The main router object
 */
var router = sUtil.router();

/**
 * The main application object reported when this module is require()d
 */
var app;

var DEBUG = true;

// gallery constants:
var MAX_ITEM_COUNT = 256;
var MIN_IMAGE_SIZE = 64;
var MAX_IMAGE_WIDTH = 1280;

function dbg(name, obj) {
    if (DEBUG) {
        console.log("DEBUG: " + name + ": " + util.inspect(obj));
        //console.log("DEBUG: " + name + ": " + JSON.stringify(obj, null, 2));
        //app.logger.log('debug', name + ": " + JSON.stringify(obj));
    }
}

/**
 * A helper function that obtains the HTML from the MW API and
 * loads it into a domino DOM document instance.
 *
 * @param {String} domain the domain to contact
 * @param {Object} params an Object with all the query parameters for the MW API
 * @return {Promise} a promise resolving as the HTML element object
 */
function apiGet(domain, params) {
    // get the page from the MW API
    return preq.get({
        uri: 'http://' + domain + '/w/api.php',
        query: params
    });
}

function rmSelectorAll(doc, selector) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.parentNode.removeChild(node);
    }
}

function rmBracketSpans(doc) {
    var ps = doc.querySelectorAll('span:not([class],[style],[id])') || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        if (node.innerHTML === '[') {
            var leftBracket = doc.createTextNode('[');
            node.parentNode.replaceChild(leftBracket, node);
        } else if (node.innerHTML === ']') {
            var rightBracket = doc.createTextNode(']');
            node.parentNode.replaceChild(rightBracket, node);
        }
    }
}

function rmAttributeAll(doc, selector, attribute) {
    var ps = doc.querySelectorAll(selector) || [];
    for (var idx = 0; idx < ps.length; idx++) {
        var node = ps[idx];
        node.removeAttribute(attribute);
    }
}

function moveFirstParagraphUpInLeadSection(text) {
    var doc = domino.createDocument(text);
    // TODO: mhurd: feel free to add your lead section magic here
    return doc.body.innerHTML;
}

/**
 * Create HTML for the section heading and edit button.
 * @param section JSON of section
 * @returns {string} HTML doc fragment
 * @see app code sections.js
 */
function buildSectionHeading(section) {
    var document = domino.createDocument();
    var tocLevel = section.toclevel || 0;
    var heading = document.createElement("h" + ( tocLevel + 1 ));
    // TODO: RTL support
    //heading.setAttribute( "dir", window.directionality );
    heading.innerHTML = typeof section.line !== "undefined" ? section.line : "";
    if (section.anchor) {
        heading.id = section.anchor;
    }
    heading.className = "section_heading";
    heading.setAttribute("data-id", section.id);

    var editButton = document.createElement("a");
    editButton.setAttribute("data-id", section.id);
    editButton.setAttribute("data-action", "edit_section");
    editButton.className = "edit_section_button";
    heading.appendChild(editButton);

    return heading.outerHTML;
}

/**
 * Nukes stuff from the DOM we don't want.
 */
function runDomTransforms(text, sectionIndex) {
    var doc = domino.createDocument(text);

    var rmSelectors = [
        'div.noprint',
        'div.infobox',
        'div.metadata',
        'table.navbox',
        'div.magnify',
        'span[style*="display:none"]',             // Remove
        'span.Z3988'                               // Remove
    ];
    if (sectionIndex === 0) {
        rmSelectors.push('div.hatnote');
    }
    rmSelectorAll(doc, rmSelectors.join(', '));    // Do single call to rmSelectorAll.

    rmAttributeAll(doc, 'a', 'rel');
    rmAttributeAll(doc, 'a,span', 'title');
    rmAttributeAll(doc, 'img', 'alt');

    rmBracketSpans(doc);

    // TODO: mhurd: add more references to functions where you do more transforms here
    //content = transformer.transform("section", content);
    //content = transformer.transform("hideTables", content);
    //content = transformer.transform("hideIPA", content);
    //content = transformer.transform("hideRefs", content );

    return doc.body.innerHTML;
}

function buildContentDiv(sectionText, sectionIndex) {
    var doc = domino.createDocument();
    var content = doc.createElement("div");
    // TODO: RTL support
    //content.setAttribute("dir", window.directionality);
    content.id = "content_block_" + sectionIndex;
    content.innerHTML = runDomTransforms(sectionText, sectionIndex);
    return content.outerHTML;
}

function checkApiResponse(response) {
    // check if the query failed
    if (response.status > 299) {
        // there was an error in the MW API, propagate that
        throw new HTTPError({
            status: response.status,
            type: 'api_error',
            title: 'MW API error',
            detail: response.body
        });
    }
}

function checkForQueryPagesIn(response) {
    if (!response.body.query || !response.body.query.pages) {
        // we did not get our expected query.pages from the MW API, propagate that
        app.logger.log('error', 'no query.pages in response: ' + JSON.stringify(response, null, 2));
        throw new HTTPError({
            status: response.status,
            type: 'api_error',
            title: 'no query.pages in response',
            detail: response.body
        });
    }
}

/**
 * Create a JS script tag inside an HTML document
 */
function embedJsScriptInHtml(doc, name, json) {
    var script = doc.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.innerHTML = "var " + name + " = " + JSON.stringify(json, null, 2);
    return script;
}

/**
 * Substitute for missing elem.insertAdjacentHTML('beforeend', str).
 */
function insertAdjacentHTML(doc, elem, str) {
    var child = doc.createElement('div');
    child.innerHTML = str;
    child = child.firstChild;
    elem.appendChild(child);
}

/**
 * Compiles the final HTML string output.
 * All sections are combined, plus two JavaScript blocks for the metadata.
 * One at the beginning, and another one at the end of the HTML body.
 * The idea of the two script blocks is to have one at the beginning, which should include
 * whatever is needed to display what's above the fold and some minor, basic metadata.
 * The rest should go to the second block.
 * Right now the two script blocks are split up in a fairly straightforward way
 * to ease coding of this service. Further optimizations are conceivable.
 * The big item is the ToC, which could go to the end as well.
 *
 * @param sections JSON: sections[]
 * @param meta1 JSON: metadata needed first
 * @param meta2 metadata needed later
 * @returns {Document.outerHTML|*|Element.outerHTML|string|exports.outerHTML|outerHTML}
 */
function compileHtml(sections, meta1, meta2) {
    var doc = domino.createDocument();
    var body = doc.querySelector('body');

    body.appendChild(embedJsScriptInHtml(doc, "app_meta1", meta1));

    for (var idx = 0; idx < sections.length; idx++) {
        var section = sections[idx];
        body.innerHTML = body.innerHTML + section.text;
        //insertAdjacentHTML(doc, body, section.text);
        //body.insertAdjacentHTML('beforeend', section.text);
        //body.insertAdjacentHTML('beforeend', "
foo
");
    }

    body.appendChild(embedJsScriptInHtml(doc, "app_meta2", meta2));

    return doc.outerHTML;
}

function buildToCJSON(sections) {
    var toc = [];
    for (var idx = 0; idx < sections.length; idx++) {
        var section = sections[idx];
        toc.push({
            id: section.id,
            toclevel: section.toclevel,
            line: section.line,
            anchor: section.anchor
        });
    }
    return toc;
}

function buildPageContentJSON(orig) {
    return {
        lastmodified: orig.lastmodified,
        id: orig.id,
        revision: orig.revision,
        displaytitle: orig.displaytitle,
        description: orig.description,
        image: orig.image,
        thumb: orig.thumb,
        protection: orig.protection,
        editable: orig.editable,
        toc: buildToCJSON(orig.sections)
    };
}

/** Returns a promise to retrieve the page content from MW API mobileview */
function pageContentPromise(domain, title) {
    return apiGet(domain, {
        "action": "mobileview",
        "format": "json",
        "page": title,
        "prop": "text|sections|thumb|image|id|revision|description|lastmodified|normalizedtitle|displaytitle|protection|editable",
        "sections": "all",
        "sectionprop": "toclevel|line|anchor",
        "noheadings": true
    }).then(function (response) {
        checkApiResponse(response);

        // transform all sections
        var sections = response.body.mobileview.sections;
        for (var idx = 0; idx < sections.length; idx++) {
            var section = sections[idx];
            var html = buildSectionHeading(section) + buildContentDiv(section.text, idx);
            if (DEBUG) {
                section.text = "\n\n\n" + html;
            } else {
                section.text = html;
            }
        }

        if (!response.body.mobileview.mainpage) {
            // don't do anything if this is the main page, since many wikis
            // arrange the main page in a series of tables.
            // TODO: should we also exclude file and other special pages?
            sections[0].text = moveFirstParagraphUpInLeadSection(sections[0].text);
        }

        return {
            sections: sections,
            json: buildPageContentJSON(response.body.mobileview)
        };
    });
}

// in the case of video, look for a list of transcodings, so that we might
// find a WebM version, which is playable in Android.
function getTranscodedVideoUrl(objinfo) {
    var derivativesArr, derivative, url, key;
    if (objinfo.derivatives) {
        derivativesArr = objinfo.derivatives;
        for (key in derivativesArr) {
            if (derivativesArr.hasOwnProperty(key)) {
                derivative = derivativesArr[key];
                if (derivative.type && derivative.type.indexOf("webm") > -1) {
                    // that's the one!
                    url = derivative.src;
                    // Note: currently picks the last one
                    // TODO: in the future we could have an extra URL that provides a size parameter for images and videos
                }
            }
        }
    }
    return url;
}

function getExtMetadata(extmetadata) {
    var ext = {};
    for (var key in extmetadata) {
        if (extmetadata.hasOwnProperty(key)) {
            var value = extmetadata[key].value;
            if (typeof value === "string") {
                ext[key] = value.trim();
            }
        }
    }
    return ext;
}

function handleGalleryItems(item) {
    var obj, url;

    if (item.imageinfo) {
        obj = item.imageinfo[0];
    } else if (item.videoinfo) {
        obj = item.videoinfo[0];
        url = getTranscodedVideoUrl(obj);
    }
    if (!url) {
        url = obj.url;
    }
    return {
        title: item.title,
        url: url,
        thumbUrl: obj.thumbUrl,
        mimeType: obj.mimeType,
        width: obj.width,
        height: obj.height,
        ext: getExtMetadata(obj.extmetadata)
    };
}

function onGalleryItemsResponse(response) {
    var items, key;
    var output = [];

    checkApiResponse(response);
    checkForQueryPagesIn(response);

    items = response.body.query.pages;
    for (key in items) {
        if (items.hasOwnProperty(key)) {
            output.push(handleGalleryItems(items[key]));
        }
    }

    return output;
}

/** Returns a promise to retrieve one or more gallery items. */
function galleryItemsPromise(domain, titles, params) {
    Object.assign(params, {
        "action": "query",
        "format": "json",
        "titles": titles,
        "continue": ""
    });

    return apiGet(domain, params)
        .then(function (response) {
            return onGalleryItemsResponse(response);
        });
}

function onGalleryCollectionsResponse(response, domain) {
    var detailsPromises = [], videos = [], images = [];
    var isVideo;

    checkApiResponse(response);
    if (!response.body.query || !response.body.query.pages) {
        return [];
    }

    // iterate over all items
    var items = response.body.query.pages;
    for (var key in items) {
        if (items.hasOwnProperty(key)) {
            var item = items[key];

            // remove the ones that are too small or are of the wrong type
            var imageinfo = item.imageinfo[0];  // TODO: why this is an array?

            // Reject gallery items if they're too small.
            // Also reject SVG and PNG items by default, because they're likely to be
            // logos and/or presentational images.
            if (imageinfo.width < MIN_IMAGE_SIZE
                || imageinfo.height < MIN_IMAGE_SIZE
                || imageinfo.mime.indexOf("svg") > -1
                || imageinfo.mime.indexOf("png") > -1
            ) {
                delete items[key];
            } else {
                delete item.ns;
                delete item.imagerepository; // we probably don't care where the repo is
                delete imageinfo.size;
                // TODO instead of deleting properties we probably want to just add well-known properties

                isVideo = imageinfo.mime.indexOf("ogg") > -1 || imageinfo.mime.indexOf("video") > -1;

                // request details individually, to keep the order
                //detailsPromises.push(galleryItemsPromise(domain, item.title, isVideo));

                if (isVideo) {
                    videos.push(item.title);
                } else {
                    images.push(item.title);
                }
            }
        }
    }

    // one more request for all the videos
    if (videos.length > 0) {
        detailsPromises.videos = galleryItemsPromise(domain, videos.join('|'), {
            "prop": "videoinfo",
            "viprop": "url|dimensions|mime|extmetadata|derivatives",
            "viurlwidth": MAX_IMAGE_WIDTH
        });
    }

    // another one request for all the images
    if (images.length > 0) {
        detailsPromises.images = galleryItemsPromise(domain, images.join('|'), {
            "prop": "imageinfo",
            "iiprop": "url|dimensions|mime|extmetadata",
            "iiurlwidth": MAX_IMAGE_WIDTH
        });
    }

    return detailsPromises;
}

/** Gets the gallery content from MW API */
function galleryCollectionPromise(domain, title) {
    return apiGet(domain, {
        "action": "query",
        "format": "json",
        "titles": title,
        "continue": "",
        "prop": "imageinfo",
        "iiprop": "dimensions|mime",
        "generator": "images",
        "gimlimit": MAX_ITEM_COUNT
    }).then(function (response) {
        var detailsPromises = onGalleryCollectionsResponse(response, domain);
        return BBPromise.props({
            videos: detailsPromises.videos,
            images: detailsPromises.images
        });
    });
}

/**
 * GET {domain}/v1/mobile/app/main/html/{title}
 * Gets the mobile app version of a given wiki page.
 */
router.get('/html/:title', function (req, res) {
    BBPromise.props({
        page: pageContentPromise(req.params.domain, req.params.title),
        media: galleryCollectionPromise(req.params.domain, req.params.title)
    }).then(function (response) {
        var html = compileHtml(response.page.sections, response.page.json, response.media);
        res.status(200).type('html').end(html);
    });
});

module.exports = function (appObj) {
    app = appObj;
    return {
        path: '/mobile/app/main',
        api_version: 1,
        router: router
    };
};

<|endoftext|>


<commit_before>import { Component } from 'react'
import MeetingPointEditButton from '../Attachments/'
import AssessorList from '../AssessorList'
import ProgressButtonBar from '../ProgressButtonBar'
import { StageDisplayNames } from '../../../constants.js'
import { convertDate } from '../../../utils.js'
import styled from 'styled-components'

var h = require('react-hyperscript')

export class AssessmentData extends Component {
  render () {
    if (this.props.invalidAssessment) {
      return h('div', 'invalid assessment address!! (maybe you are on the wrong network)')
    }
    if (this.props.loadedInfo) {
      let assessment = this.props.assessment
      let actionRequired = assessment.stage === assessment.userStage
      let nOtherAssessorsToBeActive = assessment.size - assessment.done - (actionRequired ? 1 : 0)
      let statusString = 'Waiting for ' + (actionRequired ? 'you and ' : '') + nOtherAssessorsToBeActive + ' assessors to ' + StageDisplayNames[assessment.stage]
      return (
        h(SuperFrame, [
          // holds role and concept title
          h(assessmentHeader, [
            h(assessmentLabelRole, assessment.assessee !== this.props.userAddress ? 'Assessing' : 'Getting assessed in'),
            h(assessmentTextTitle, assessment.conceptData),
            h(assessmentAddressInHeader, assessment.address)
          ]),
          // indicates status of assesssment
          h(assessmentRowSubHeader, [
            h(assessmentContainerStatus, [
              h(assessmentLabelBody, 'STATUS'),
              h(assessmentTextBody, statusString)
            ]),
            h(assessmentContainerDate, [
              h(assessmentLabelBody, 'Due Date:'),
              h(assessmentTextBody, convertDate(assessment.endTime))
            ])
          ]),
          // basic info
          h(assessmentContainerBody, [
            h(assessmentColumnLeft, [
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Assessee'),
                h(assessmentTextBody, assessment.assessee)
              ]),
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Fee'),
                h(assessmentTextBody, assessment.cost + 'AHA')
              ]),
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Meeting Point'),
                h(assessmentTextBody, assessment.data || 'You haven\'t set a meeting point'),
                h(assessmentRow, [
                  assessment.data !== ''
                    ? h(fathomButtonPrimary, {href: assessment.data, disabled: assessment.data === ''}, 'View')
                    : null,
                  assessment.assessee === this.props.userAddress
                    ? h(MeetingPointEditButton, {assessee: assessment.assessee})
                    : null
                ])
              ])
            ]),
            h(assessmentColumnRight, [
              h(assessmentObjectTextRight, [
                h(assessmentLabelBody, 'Assessors'),
                h(assessmentObjectText),
                h(AssessorList)
              ])
            ])
          ]),
          // progress-buttons
          h(assessmentFooter, [
            h(ProgressButtonBar)
          ])
        ])
      )
    } else {
      return h('div', 'Loading Data')
    }
  }
}

const SuperFrame = styled('div').attrs({className: 'flex flex-column w-100 mw8 bg-white shadow-4'})`
font-family:'system-ui',sans-serif;
`

const assessmentHeader = styled('div').attrs({className: 'flex flex-column w-100 pv4 ph3 bg-lightest-blue'})`
`

const assessmentLabelRole = styled('h6').attrs({className: 'f6 tl ttu uppercase dark-blue mv0 fw4'})`
`

const assessmentTextTitle = styled('h2').attrs({className: 'f2 tl ttu uppercase dark-blue mt2 mb0 fw4'})`
`

const assessmentAddressInHeader = styled('h5').attrs({className: 'f2 tl ttu uppercase dark-blue mt2 mb0 fw4'})`
`

const assessmentRowSubHeader = styled('div').attrs({className: 'flex flex-row w-100 items-center bt bb b--gray'})`
`

const assessmentContainerStatus = styled('div').attrs({className: 'flex flex-row w-50 items-center justify-between pa3'})`
`

const assessmentLabelBody = styled('h6').attrs({className: 'f6 mid-gray mv0 fw4 ttu uppercase'})`
`

const assessmentTextBody = styled('h5').attrs({className: 'f5 gray mt2 mb0 fw4 gray'})`
`

const assessmentContainerDate = styled('div').attrs({className: 'flex flex-row w-50 items-center justify-between pa3 bl b--gray'})`
`

const assessmentContainerBody = styled('div').attrs({className: 'flex flex-row w-100 items-center justify-between'})`
`

const assessmentColumnLeft = styled('div').attrs({className: 'flex flex-column w-50 items-around justify-around pa3'})`
`

const assessmentObjectText = styled('div').attrs({className: 'flex flex-column w-100  items-start justify-center self-start mv3'})`
`

const assessmentObjectTextRight = styled('div').attrs({className: 'flex flex-column w-100  items-end justify-center self-start mv3'})`
`

const assessmentListAssessors = styled('div').attrs({className: 'flex flex-column w-100 h-100 self-start  items-start justify-center self-start mv3'})`
`

const assessmentColumnRight = styled('div').attrs({className: 'flex flex-column w-50 h-100 self-start items-start justify-around pa3'})`
`

const assessmentRow = styled('div').attrs({className: 'flex flex-row w-100 mw5 justify-between mt3 '})`
`

const fathomButtonPrimary = styled('button').attrs({className: 'flex self-end ph4 pv2 fw4 f5 shadow-4 items-center align-center br-pill bg-dark-blue near-white ttu uppercase'})`
`

const assessmentFooter = styled('div').attrs({className: 'flex flex-row w-100'})`
`

export default AssessmentData
<commit_msg>Resolved linting issues<commit_after>import { Component } from 'react'
import MeetingPointEditButton from '../Attachments/'
import ProgressButtonBar from '../ProgressButtonBar'
import { StageDisplayNames } from '../../../constants.js'
import { convertDate } from '../../../utils.js'
import styled from 'styled-components'

var h = require('react-hyperscript')

export class AssessmentData extends Component {
  render () {
    if (this.props.invalidAssessment) {
      return h('div', 'invalid assessment address!! (maybe you are on the wrong network)')
    }
    if (this.props.loadedInfo) {
      let assessment = this.props.assessment
      let actionRequired = assessment.stage === assessment.userStage
      let nOtherAssessorsToBeActive = assessment.size - assessment.done - (actionRequired ? 1 : 0)
      let statusString = 'Waiting for ' + (actionRequired ? 'you and ' : '') + nOtherAssessorsToBeActive + ' assessors to ' + StageDisplayNames[assessment.stage]
      return (
        h(SuperFrame, [
          // holds role and concept title
          h(assessmentHeader, [
            h(assessmentLabelRole, assessment.assessee !== this.props.userAddress ? 'Assessing' : 'Getting assessed in'),
            h(assessmentTextTitle, assessment.conceptData)
          ]),
          // indicates status of assesssment
          h(assessmentRowSubHeader, [
            h(assessmentContainerStatus, [
              h(assessmentLabelBody, 'STATUS'),
              h(assessmentTextBody, statusString)
            ]),
            h(assessmentContainerDate, [
              h(assessmentLabelBody, 'Due Date:'),
              h(assessmentTextBody, convertDate(assessment.endTime))
            ])
          ]),
          // basic info
          h(assessmentContainerBody, [
            h(assessmentColumnLeft, [
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Assessee'),
                h(assessmentTextBody, assessment.assessee)
              ]),
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Fee'),
                h(assessmentTextBody, assessment.cost + 'AHA')
              ]),
              h(assessmentObjectText, [
                h(assessmentLabelBody, 'Meeting Point'),
                h(assessmentTextBody, assessment.data || 'You haven\'t set a meeting point'),
                h(assessmentRow, [
                  h(fathomButtonPrimary, {href: assessment.data, disabled: assessment.data === ''}, 'View'),
                  assessment.assessee === this.props.userAddress
                    ? h(MeetingPointEditButton, {className: fathomButtonSecondary, assessee: assessment.assessee}) // TODO fathomButtonSecondary should replace 'h(MeetingPointEditButton'
                    : null
                ])
              ])
            ]),
            h(assessmentColumnRight, [
              h(assessmentObjectTextRight, [
                h(assessmentLabelBody, 'Assessors'),
                h(assessmentListAssessors)
              ])
            ])
          ]),
          // progress-buttons
          h(assessmentFooter, [
            h(ProgressButtonBar)
          ])
        ])
      )
    } else {
      return h('div', 'Loading Data')
    }
  }
}

const SuperFrame = styled('div').attrs({className: 'flex flex-column w-100 mw8 bg-white shadow-4'})`
font-family:'system-ui',sans-serif;
`

const assessmentHeader = styled('div').attrs({className: 'flex flex-column w-100 pv4 ph3 bg-lightest-blue'})`
`

const assessmentLabelRole = styled('h6').attrs({className: 'f6 tl ttu uppercase dark-blue mv0 fw4'})`
`

const assessmentTextTitle = styled('h2').attrs({className: 'f2 tl ttu uppercase dark-blue mt2 mb0 fw4'})`
`

const assessmentRowSubHeader = styled('div').attrs({className: 'flex flex-row w-100 items-center bt bb b--gray'})`
`

const assessmentContainerStatus = styled('div').attrs({className: 'flex flex-row w-50 items-center justify-between pa3'})`
`

const assessmentLabelBody = styled('h6').attrs({className: 'f6 mid-gray mv0 fw4 ttu uppercase'})`
`

const assessmentTextBody = styled('h5').attrs({className: 'f5 gray mt2 mb0 fw4 gray'})`
`

const assessmentContainerDate = styled('div').attrs({className: 'flex flex-row w-50 items-center justify-between pa3 bl b--gray'})`
`

const assessmentContainerBody = styled('div').attrs({className: 'flex flex-row w-100 items-center justify-between'})`
`

const assessmentColumnLeft = styled('div').attrs({className: 'flex flex-column w-50 items-around justify-around pa3'})`
`

const assessmentObjectText = styled('div').attrs({className: 'flex flex-column w-100  items-start justify-center self-start mv3'})`
`

const assessmentObjectTextRight = styled('div').attrs({className: 'flex flex-column w-100  items-end justify-center self-start mv3'})`
`

const assessmentListAssessors = styled('div').attrs({className: 'flex flex-column w-100 h-100 self-start  items-start justify-center self-start mv3'})`
`

const assessmentColumnRight = styled('div').attrs({className: 'flex flex-column w-50 h-100 self-start items-start justify-around pa3'})`
`

const assessmentRow = styled('div').attrs({className: 'flex flex-row w-100 mw5 justify-between mt3 '})`
`

const fathomButtonPrimary = styled('button').attrs({className: 'flex self-end ph4 pv2 fw4 f5 shadow-4 items-center align-center br-pill bg-dark-blue near-white ttu uppercase'})`
`

const fathomButtonSecondary = styled('button').attrs({className: 'flex self-start ph4 pv2 fw4 f5 items-center align-center br-pill dark-blue'})`
box-shadow: 0px 0px 0px 1px hsla(214, 100%, 31%, 0.1);
`

const assessmentFooter = styled('div').attrs({className: 'flex flex-row w-100'})`
`

export default AssessmentData
<|endoftext|>


<commit_before>/*global $, _gaq, unescape, escape, search*/

require(['search'], function (Search) {
    /* Determine if `text` is a char-code short-cut of some sort or a plain
     * search-term.
     */
    function isCharCode(text) {
        var sourceMatch = text.match(/^\\u([\dABCDEFabcdef]{4})$/),
            codePointMatch = text.match(/^&#(\d+);$/) || text.match(/^(\d+)$/),
            code = undefined;

        if (sourceMatch) {
            code = parseInt(sourceMatch[1], 16);
        } else if (codePointMatch) {
            code = codePointMatch[1];
        } else if (text.length === 1) {
            code = text.charCodeAt(0);
        }

        return code;
    }

    function highSurrogate(codePoint) {
        return Math.floor((codePoint - 0x10000) / 0x400) + 0xD800;
    }

    function lowSurrogate(codePoint) {
        return (codePoint - 0x10000) % 0x400 + 0xDC00;
    }

    function unicodeEscape(codePoint) {
        if (codePoint <= 0xFFFF) {
            return '\\u' + (codePoint + 0x10000).toString(16).substr(-4).toUpperCase();
        } else {
            throw 'Code point outside BMP cannot be encoded.';
        }
    }

    function codePointToString(codePoint) {
        if (codePoint <= 0xFFFF) {
            // Anything inside the BMP can be escaped as usual
            return unicodeEscape(codePoint);
        } else {
            // Outside the BMP we generate surrogate pairs
            return unicodeEscape(highSurrogate(codePoint)) + unicodeEscape(lowSurrogate(codePoint));
        }
    }

    document.getElementById('searchField').focus();

    // Block form submission. Search will be activated when data is downloaded
    document.forms[0].onsubmit = function () {
        return false;
    };

    _gaq = window._gaq || [];
    _gaq.push(['_setAccount', 'UA-33198175-1']);
    _gaq.push(['_setDomainName', 'charcod.es']);
    _gaq.push(['_trackPageview']);

    var unicodeTable = {},
        popup = (function () {
            var active,
                info,
                getTpl = function (codePoint) {
                    var info = unicodeTable[codePoint],
                        tpl = $('.templates .charInfo').clone(),
                        combining = unicodeTable[codePoint] && unicodeTable[codePoint].n.indexOf('combining') !== -1,
                        htmlCodes = [ '&#' + codePoint + ';' ];

                    // Set location-hash intelligently
                    var searchTerm = $('#searchField').val(),
                        permaLink = searchTerm + '/' + codePoint,
                        permaLinkChar = codePoint;
                    if (searchTerm === codePoint) {
                        permaLink = codePoint;
                    }
                    location.hash = '#' + permaLink;

                    tpl.find('h2').html((combining ? "◌" : "") + '&#' + codePoint + ';');
                    tpl.find('.permalink').attr('href', '#' + permaLinkChar);

                    // Source code name
                    tpl.find('.char-source').html(codePointToString(parseInt(codePoint, 10)));
                    // Code point
                    tpl.find('.char-codepoint').html(codePoint);

                    if (info) {
                        tpl.find('h3').html(info.n);
                        // TODO: Just changes URL - doesn't currently act on it!
                        tpl.find('.char-group')
                            .attr('href', '#' + info.b)
                            .html(info.b);
                        if (info.a && info.a.length > 0) {
                            tpl.find(".aliases").html(info.a.join(', '));
                        }

                        // Set HTML names
                        if (info.altnames && info.altnames.html) {
                            htmlCodes.push('&#' + info.altnames.html + ';');
                        }
                        tpl.find('.char-html').html(htmlCodes.join("
"));

                        // Track the group/block of what chars people are clicking.
                        _gaq.push(['_trackEvent', 'popup', 'activate', info.b]);

                        // LaTeX names
                        if (info.altnames && info.altnames.latex) {
                            tpl.find('.char-latex').html(info.altnames.latex);
                        } else {
                            tpl.find('.char-latex-row').hide();
                        }
                    }

                    return tpl;
                };

            return {
                activate: function (elm) {
                    var codePoint = elm[0].id.replace('id', ''),
                        offset = elm.position().top,
                        currentElement = elm;

                    if (active) {
                        this.deactivate();
                    }
                    active = elm;
                    info = getTpl(codePoint);

                    while (currentElement.next().length && currentElement.next().position().top === offset) {
                        currentElement = currentElement.next();
                    }
                    info.insertAfter(currentElement);

                    active.addClass('active');

                },

                deactivate: function () {
                    _gaq.push(['_trackEvent', 'popup', 'deactivate']);
                    active.removeClass('active');
                    if (info) {
                        info.remove();
                    }
                    active = info = undefined;
                }
            };
        }());

    // Click-handler that show a modal dialog
    $('#results').on('click', 'a', function (e) {
        popup.activate($(e.target));
        e.preventDefault();
    });

    // Handle search input
    var timeout,
        searchField = $('#searchField');

    searchField.keyup(function (e) {
        if (timeout) {
            window.clearTimeout(timeout);
        }
        timeout = window.setTimeout(function () {
            searchAndShow(searchField.val().trim());
        }, 250);
    });

    $('.form-search').submit(function () {
        searchAndShow(searchField.val().trim());
        return false;
    });

    var search = new Search();

    // {{{ load data
    $.ajax({
        url: 'data.json',
        dataType: 'json',
        success: function (data) {
            var i,
                len = data.length,
                unicodeChar,
                indices,
                name;

            for (i = 0; i < len; i += 1) {
                unicodeChar = data[i];
                unicodeTable[unicodeChar.c] = unicodeChar;

                // Fill 'indices' with stuff I want to search on
                indices = [].concat(
                    unicodeChar.n.split(" "), // Name
                    [unicodeChar.b], // Block name
                    unicodeChar.a ? unicodeChar.a : []  // Any aliases
                    //unicodeChar.c ? unicodeChar.c : [],  // Any aliases
                );

                // Index alternative names
                if (unicodeChar.altnames) {
                    for (name in unicodeChar.altnames) {
                        indices.push(unicodeChar.altnames[name]);
                    }
                }

                search.addTerm(indices, unicodeChar.c);
            }

            // Search right away if the user has already typed something
            if (searchField.val()) {
                searchAndShow(searchField.val());
            }

            // Re-apply location hash, so stuff will show up.
            applyLocationHash();
        },
        error: function () {
            throw new Error("could not load data.json: " + arguments);
        }
    });
    // }}}

    // {{{ searchAndShow(text)
    function searchAndShow(text) {
        _gaq.push(['_trackEvent', 'search', undefined, text]);
        var codes = [],
            sourceMatch = text.match(/^\\u(\d{4})$/),
            codePointMatch = text.match(/^&#(\d+);$/) || text.match(/^(\d+)$/);

        if (sourceMatch) {
            codes = [parseInt(sourceMatch[1], 16)];
        } else if (codePointMatch) {
            codes = [codePointMatch[1]];
        } else {
            codes = search.search(text);
        }

        //console.log(codes);
        if (text.length === 1 && codes.length === 0) {
            // Assume unicode input
            codes = [text.charCodeAt(0)];
        }

        // Show help?
        if (codes.length === 0) {
            $("#help").show();
        } else {
            $("#help").hide();
        }

        $("#results").html($.map(codes, function (code) {
            var combining = unicodeTable[code] && unicodeTable[code].n.indexOf('combining') !== -1,
                outHtml = (combining ? "◌" : "") + "&#" + code + ";";

            return '' + outHtml + '';
        }).join(""));

        // If there is only one result, show details right away
        if (codes.length === 1) {
            popup.activate($('#results a').first());
        }
    }
    // }}} searchAndShow(text)

    // {{{ applyLocationHash()
    function applyLocationHash() {
        if (!location.hash) {
            return;
        }
        var h = location.hash.split('#')[1];

        // Two elements → search-term/char-to-highlight
        // One element → char-to-highlight (if it matches a char)
        //               otherwise, consider it a search term

        var test = h.match(/^([^\/]+)\/(.*)$/),
            isCode = isCharCode(h),
            searchTerm = undefined,
            highlight = undefined;

        if (test) {
            searchTerm = test[1];
            highlight = test[2];
        } else if (isCode) {
            searchTerm = highlight = isCode;
        } else {
            searchTerm = h;
            highlight = undefined;
        }

        if (highlight && searchTerm === undefined) {
            searchTerm = highlight;
        }

        searchField.val(searchTerm);
        searchAndShow(searchTerm);

        // Find the 'highlight'-element and activate the popup
        if (highlight) {
            var elem = $('#id' + highlight).first();
            if (elem) {
                popup.activate(elem);
            }
        }
    }
    // }}} applyLocationHash()

    window.onhashchange = function (changeEvent) {
        if (changeEvent.newURL !== changeEvent.oldURL) {
            applyLocationHash();
        }
    };
    applyLocationHash();
});
<commit_msg>Don't include hashtag in altnames<commit_after>/*global $, _gaq, unescape, escape, search*/

require(['search'], function (Search) {
    /* Determine if `text` is a char-code short-cut of some sort or a plain
     * search-term.
     */
    function isCharCode(text) {
        var sourceMatch = text.match(/^\\u([\dABCDEFabcdef]{4})$/),
            codePointMatch = text.match(/^&#(\d+);$/) || text.match(/^(\d+)$/),
            code = undefined;

        if (sourceMatch) {
            code = parseInt(sourceMatch[1], 16);
        } else if (codePointMatch) {
            code = codePointMatch[1];
        } else if (text.length === 1) {
            code = text.charCodeAt(0);
        }

        return code;
    }

    function highSurrogate(codePoint) {
        return Math.floor((codePoint - 0x10000) / 0x400) + 0xD800;
    }

    function lowSurrogate(codePoint) {
        return (codePoint - 0x10000) % 0x400 + 0xDC00;
    }

    function unicodeEscape(codePoint) {
        if (codePoint <= 0xFFFF) {
            return '\\u' + (codePoint + 0x10000).toString(16).substr(-4).toUpperCase();
        } else {
            throw 'Code point outside BMP cannot be encoded.';
        }
    }

    function codePointToString(codePoint) {
        if (codePoint <= 0xFFFF) {
            // Anything inside the BMP can be escaped as usual
            return unicodeEscape(codePoint);
        } else {
            // Outside the BMP we generate surrogate pairs
            return unicodeEscape(highSurrogate(codePoint)) + unicodeEscape(lowSurrogate(codePoint));
        }
    }

    document.getElementById('searchField').focus();

    // Block form submission. Search will be activated when data is downloaded
    document.forms[0].onsubmit = function () {
        return false;
    };

    _gaq = window._gaq || [];
    _gaq.push(['_setAccount', 'UA-33198175-1']);
    _gaq.push(['_setDomainName', 'charcod.es']);
    _gaq.push(['_trackPageview']);

    var unicodeTable = {},
        popup = (function () {
            var active,
                info,
                getTpl = function (codePoint) {
                    var info = unicodeTable[codePoint],
                        tpl = $('.templates .charInfo').clone(),
                        combining = unicodeTable[codePoint] && unicodeTable[codePoint].n.indexOf('combining') !== -1,
                        htmlCodes = [ '&#' + codePoint + ';' ];

                    // Set location-hash intelligently
                    var searchTerm = $('#searchField').val(),
                        permaLink = searchTerm + '/' + codePoint,
                        permaLinkChar = codePoint;
                    if (searchTerm === codePoint) {
                        permaLink = codePoint;
                    }
                    location.hash = '#' + permaLink;

                    tpl.find('h2').html((combining ? "◌" : "") + '&#' + codePoint + ';');
                    tpl.find('.permalink').attr('href', '#' + permaLinkChar);

                    // Source code name
                    tpl.find('.char-source').html(codePointToString(parseInt(codePoint, 10)));
                    // Code point
                    tpl.find('.char-codepoint').html(codePoint);

                    if (info) {
                        tpl.find('h3').html(info.n);
                        // TODO: Just changes URL - doesn't currently act on it!
                        tpl.find('.char-group')
                            .attr('href', '#' + info.b)
                            .html(info.b);
                        if (info.a && info.a.length > 0) {
                            tpl.find(".aliases").html(info.a.join(', '));
                        }

                        // Set HTML names
                        if (info.altnames && info.altnames.html) {
                            htmlCodes.push('&' + info.altnames.html + ';');
                        }
                        tpl.find('.char-html').html(htmlCodes.join("
"));

                        // Track the group/block of what chars people are clicking.
                        _gaq.push(['_trackEvent', 'popup', 'activate', info.b]);

                        // LaTeX names
                        if (info.altnames && info.altnames.latex) {
                            tpl.find('.char-latex').html(info.altnames.latex);
                        } else {
                            tpl.find('.char-latex-row').hide();
                        }
                    }

                    return tpl;
                };

            return {
                activate: function (elm) {
                    var codePoint = elm[0].id.replace('id', ''),
                        offset = elm.position().top,
                        currentElement = elm;

                    if (active) {
                        this.deactivate();
                    }
                    active = elm;
                    info = getTpl(codePoint);

                    while (currentElement.next().length && currentElement.next().position().top === offset) {
                        currentElement = currentElement.next();
                    }
                    info.insertAfter(currentElement);

                    active.addClass('active');

                },

                deactivate: function () {
                    _gaq.push(['_trackEvent', 'popup', 'deactivate']);
                    active.removeClass('active');
                    if (info) {
                        info.remove();
                    }
                    active = info = undefined;
                }
            };
        }());

    // Click-handler that show a modal dialog
    $('#results').on('click', 'a', function (e) {
        popup.activate($(e.target));
        e.preventDefault();
    });

    // Handle search input
    var timeout,
        searchField = $('#searchField');

    searchField.keyup(function (e) {
        if (timeout) {
            window.clearTimeout(timeout);
        }
        timeout = window.setTimeout(function () {
            searchAndShow(searchField.val().trim());
        }, 250);
    });

    $('.form-search').submit(function () {
        searchAndShow(searchField.val().trim());
        return false;
    });

    var search = new Search();

    // {{{ load data
    $.ajax({
        url: 'data.json',
        dataType: 'json',
        success: function (data) {
            var i,
                len = data.length,
                unicodeChar,
                indices,
                name;

            for (i = 0; i < len; i += 1) {
                unicodeChar = data[i];
                unicodeTable[unicodeChar.c] = unicodeChar;

                // Fill 'indices' with stuff I want to search on
                indices = [].concat(
                    unicodeChar.n.split(" "), // Name
                    [unicodeChar.b], // Block name
                    unicodeChar.a ? unicodeChar.a : []  // Any aliases
                    //unicodeChar.c ? unicodeChar.c : [],  // Any aliases
                );

                // Index alternative names
                if (unicodeChar.altnames) {
                    for (name in unicodeChar.altnames) {
                        indices.push(unicodeChar.altnames[name]);
                    }
                }

                search.addTerm(indices, unicodeChar.c);
            }

            // Search right away if the user has already typed something
            if (searchField.val()) {
                searchAndShow(searchField.val());
            }

            // Re-apply location hash, so stuff will show up.
            applyLocationHash();
        },
        error: function () {
            throw new Error("could not load data.json: " + arguments);
        }
    });
    // }}}

    // {{{ searchAndShow(text)
    function searchAndShow(text) {
        _gaq.push(['_trackEvent', 'search', undefined, text]);
        var codes = [],
            sourceMatch = text.match(/^\\u(\d{4})$/),
            codePointMatch = text.match(/^&#(\d+);$/) || text.match(/^(\d+)$/);

        if (sourceMatch) {
            codes = [parseInt(sourceMatch[1], 16)];
        } else if (codePointMatch) {
            codes = [codePointMatch[1]];
        } else {
            codes = search.search(text);
        }

        //console.log(codes);
        if (text.length === 1 && codes.length === 0) {
            // Assume unicode input
            codes = [text.charCodeAt(0)];
        }

        // Show help?
        if (codes.length === 0) {
            $("#help").show();
        } else {
            $("#help").hide();
        }

        $("#results").html($.map(codes, function (code) {
            var combining = unicodeTable[code] && unicodeTable[code].n.indexOf('combining') !== -1,
                outHtml = (combining ? "◌" : "") + "&#" + code + ";";

            return '' + outHtml + '';
        }).join(""));

        // If there is only one result, show details right away
        if (codes.length === 1) {
            popup.activate($('#results a').first());
        }
    }
    // }}} searchAndShow(text)

    // {{{ applyLocationHash()
    function applyLocationHash() {
        if (!location.hash) {
            return;
        }
        var h = location.hash.split('#')[1];

        // Two elements → search-term/char-to-highlight
        // One element → char-to-highlight (if it matches a char)
        //               otherwise, consider it a search term

        var test = h.match(/^([^\/]+)\/(.*)$/),
            isCode = isCharCode(h),
            searchTerm = undefined,
            highlight = undefined;

        if (test) {
            searchTerm = test[1];
            highlight = test[2];
        } else if (isCode) {
            searchTerm = highlight = isCode;
        } else {
            searchTerm = h;
            highlight = undefined;
        }

        if (highlight && searchTerm === undefined) {
            searchTerm = highlight;
        }

        searchField.val(searchTerm);
        searchAndShow(searchTerm);

        // Find the 'highlight'-element and activate the popup
        if (highlight) {
            var elem = $('#id' + highlight).first();
            if (elem) {
                popup.activate(elem);
            }
        }
    }
    // }}} applyLocationHash()

    window.onhashchange = function (changeEvent) {
        if (changeEvent.newURL !== changeEvent.oldURL) {
            applyLocationHash();
        }
    };
    applyLocationHash();
});
<|endoftext|>


<commit_before>
//apply indentation!

function indent (line) {
  return /^\s*$/.test(line) ? line : '  ' + line
}

function deindent (line) {
  var l = /^\s*/.exec(line)[0].length
  return line.substring(l > 1 ? 2 : l)
}

module.exports = function (doc, keys) {

  var rc = this.config

  keys.on('keypress', function (ch, key) {

    if(key.name !== 'tab') return

    // apply indentation to selected lines.
    if(doc.marks) {
      console.error('INDENT SELECTION')
      var m = doc.marks[0]
      var M = doc.marks[1]

      for(var i = m.y; i <= M.y; i++) {
        doc.updateLine(i,
          (key.shift ? deindent : indent) (doc.lines[i])
        )
      }

    }
    else {
      //pad current line to an even number of spaces.
      var m
      var i = (m = /^\s*/.exec(doc.line()))[0].length
      console.error('INDENT SINGLE LINE', i, m)

      doc.pos(i, doc.row) //move to the end of the indentation.

      if(!key.shift)
        doc.write(i % 2 ? ' ' : '  ')
      else  if(i) doc.delete(i % 2 ? -1 : -2)

      // add spaces.
//      else
//        doc.updateLine(doc.row, (i % 2 ? ' ' : '  ') + doc.line())

//      doc.move()//something is putting the cursor in the wrong place.
    }

    //Refactor this when there is another command that needs to veto something.
    //vvv TOTALLY HIDEOUS WAY TO PREVENT OTHER COMMANDS TRIGGERING.
     key.name = 'VETOED'
  })
}

<commit_msg>do not consider \n to be indentation<commit_after>
//apply indentation!

function indent (line) {
  return /^\s*$/.test(line) ? line : '  ' + line
}

function deindent (line) {
  var l = /^\s*/.exec(line)[0].length
  return line.substring(l > 1 ? 2 : l)
}

module.exports = function (doc, keys) {

  var rc = this.config

  keys.on('keypress', function (ch, key) {

    if(key.name !== 'tab') return

    // apply indentation to selected lines.
    if(doc.marks) {
      console.error('INDENT SELECTION')
      var m = doc.marks[0]
      var M = doc.marks[1]

      for(var i = m.y; i <= M.y; i++) {
        doc.updateLine(i,
          (key.shift ? deindent : indent) (doc.lines[i])
        )
      }

    }
    else {
      //pad current line to an even number of spaces.
      var m
      var i = (m = /^[ \t]*/.exec(doc.line()))[0].length
      console.error('INDENT SINGLE LINE', i, m)

      doc.pos(i, doc.row) //move to the end of the indentation.

      if(!key.shift)
        doc.write(i % 2 ? ' ' : '  ')
      else  if(i) doc.delete(i % 2 ? -1 : -2)

      // add spaces.
//      else
//        doc.updateLine(doc.row, (i % 2 ? ' ' : '  ') + doc.line())

//      doc.move()//something is putting the cursor in the wrong place.
    }

    //Refactor this when there is another command that needs to veto something.
    //vvv TOTALLY HIDEOUS WAY TO PREVENT OTHER COMMANDS TRIGGERING.
     key.name = 'VETOED'
  })
}

<|endoftext|>


<commit_before>/* global FileReader */
import { Template } from 'meteor/templating';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { AcademicYearInstances } from '../../../api/year/AcademicYearInstanceCollection';
import { CareerGoals } from '../../../api/career/CareerGoalCollection';
import { CourseInstances } from '../../../api/course/CourseInstanceCollection';
import { DesiredDegrees } from '../../../api/degree/DesiredDegreeCollection';
import { Interests } from '../../../api/interest/InterestCollection.js';
import { Semesters } from '../../../api/semester/SemesterCollection';
import { Slugs } from '../../../api/slug/SlugCollection.js';
import { Users } from '../../../api/user/UserCollection.js';
import { ROLE, ROLES } from '../../../api/role/Role.js';
import * as FormUtils from '../admin/form-fields/form-field-utilities.js';
import * as planUtils from '../../../api/degree-program/plan-generator';
import * as semUtils from '../../../api/semester/SemesterUtilities';
import * as courseUtils from '../../../api/course/CourseFunctions';
import * as opportunityUtils from '../../../api/opportunity/OpportunityFunctions';
import { _ } from 'meteor/erasaur:meteor-lodash';

const updateSchema = new SimpleSchema({
  firstName: { type: String, optional: false },
  lastName: { type: String, optional: false },
  slug: { type: String, optional: false }, // will rename this to username
  role: { type: String, optional: false },
  email: { type: String, optional: false },
  uhID: { type: String, optional: false },
  // remaining are optional.
  desiredDegree: { type: String, optional: true },
  picture: { type: String, optional: true },
  level: { type: Number, optional: true },
  careerGoals: { type: [String], optional: true },
  interests: { type: [String], optional: true },
  website: { type: String, optional: true },
});

Template.Update_Degree_Plan_Widget.helpers({
  user() {
    if (Template.currentData().studentID.get()) {
      return Users.findDoc(Template.currentData().studentID.get());
    }
    return '';
  },
  interests() {
    return Interests.find({}, { sort: { name: 1 } });
  },
  careerGoals() {
    return CareerGoals.find({}, { sort: { name: 1 } });
  },
  desiredDegrees() {
    return DesiredDegrees.find({}, { sort: { name: 1 } });
  },
  roles() {
    return _.sortBy(_.difference(ROLES, [ROLE.ADMIN]));
  },
  slug() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return Slugs.findDoc(user.slugID).name;
    }
    return '';
  },
  semesters() {
    const currentSemester = Semesters.getCurrentSemesterDoc();
    return _.filter(Semesters.find({ sortBy: { $gte: currentSemester.sortBy } }, { sort: { sortBy: 1 } }).fetch(),
        function notSummer(s) {
          return s.term !== Semesters.SUMMER;
        });
  },
  selectedInterestIDs() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.interestIDs;
    }
    return '';
  },
  selectedCareerGoalIDs() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.careerGoalIDs;
    }
    return '';
  },
  selectedDesiredDegreeID() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.desiredDegreeID;
    }
    return '';
  },
  selectedRole() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      console.log('selectedRole', user.roles[0]);
      return user.roles[0];
    }
    return '';
  },
});

Template.Update_Degree_Plan_Widget.events({
  'click .jsGeneratePlan': function clickGeneratePlan(event, instance) {
    event.preventDefault();
    instance.$('.ui.modal').modal({
      onDeny: function () {
        console.log(instance);
      },
      onApprove: function () {
        const studentID = instance.data.studentID.get();
        const student = Users.findDoc(studentID);
        const currentSemester = Semesters.getCurrentSemesterDoc();
        const selectedSemesterID = instance.$('#planningSemester').val();
        console.log('selectedSemesterID', selectedSemesterID);
        // let startSemester;
        // if () {
        //   startSemester = Semesters.findDoc()
        // }
        // if (!startSemester) {
        //   startSemester = currentSemester;
        // }
        // if (currentSemester.sortBy === startSemester.sortBy) {
        //   startSemester = semUtils.nextFallSpringSemester(startSemester);
        // }
        // // TODO: CAM do we really want to blow away the student's plan. What if they've made changes?
        // courseUtils.clearPlannedCourseInstances(studentID);
        // opportunityUtils.clearPlannedOpportunityInstances(studentID);
        // const cis = CourseInstances.find({ studentID }).fetch();
        // const ays = AcademicYearInstances.find({ studentID }).fetch();
        // if (cis.length === 0) {
        //   _.map(ays, (year) => {
        //     AcademicYearInstances.removeIt(year._id);
        //   });
        // } else {
        //   // TODO: CAM figure out which AYs to remove.
        // }
        // if (student.desiredDegreeID) {
        //   const degree = DesiredDegrees.findDoc({ _id: student.desiredDegreeID });
        //   if (degree.shortName.startsWith('B.S.')) {
        //     planUtils.generateBSDegreePlan(student, startSemester);
        //   }
        //   if (degree.shortName.startsWith('B.A.')) {
        //     planUtils.generateBADegreePlan(student, startSemester);
        //   }
        // }
      },
    }).modal('show');
  },
});

Template.Update_Degree_Plan_Widget.onCreated(function updateDegreePlanWidgetOnCreated() {
  FormUtils.setupFormWidget(this, updateSchema);
  this.subscribe(AcademicYearInstances.getPublicationName());
  this.subscribe(CareerGoals.getPublicationName());
  this.subscribe(CourseInstances.getPublicationName());
  this.subscribe(DesiredDegrees.getPublicationName());
  this.subscribe(Semesters.getPublicationName());
  this.subscribe(Slugs.getPublicationName());
  this.subscribe(Interests.getPublicationName());
  this.subscribe(Users.getPublicationName());
});

Template.Update_Degree_Plan_Widget.onRendered(function updateDegreePlanWidgetOnRendered() {
  // add your statement here
});

Template.Update_Degree_Plan_Widget.onDestroyed(function updateDegreePlanWidgetOnDestroyed() {
  // add your statement here
});

<commit_msg>Now creating valid plans again.<commit_after>/* global FileReader */
import { Template } from 'meteor/templating';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { AcademicYearInstances } from '../../../api/year/AcademicYearInstanceCollection';
import { CareerGoals } from '../../../api/career/CareerGoalCollection';
import { CourseInstances } from '../../../api/course/CourseInstanceCollection';
import { Courses } from '../../../api/course/CourseCollection';
import { DesiredDegrees } from '../../../api/degree/DesiredDegreeCollection';
import { Interests } from '../../../api/interest/InterestCollection.js';
import { OpportunityInstances } from '../../../api/opportunity/OpportunityInstanceCollection';
import { OpportunityTypes } from '../../../api/opportunity/OpportunityTypeCollection';
import { Opportunities } from '../../../api/opportunity/OpportunityCollection';
import { Semesters } from '../../../api/semester/SemesterCollection';
import { Slugs } from '../../../api/slug/SlugCollection.js';
import { Users } from '../../../api/user/UserCollection.js';
import { ROLE, ROLES } from '../../../api/role/Role.js';
import * as FormUtils from '../admin/form-fields/form-field-utilities.js';
import * as planUtils from '../../../api/degree-program/plan-generator';
import * as semUtils from '../../../api/semester/SemesterUtilities';
import * as courseUtils from '../../../api/course/CourseFunctions';
import * as opportunityUtils from '../../../api/opportunity/OpportunityFunctions';
import { _ } from 'meteor/erasaur:meteor-lodash';

const updateSchema = new SimpleSchema({
  firstName: { type: String, optional: false },
  lastName: { type: String, optional: false },
  slug: { type: String, optional: false }, // will rename this to username
  role: { type: String, optional: false },
  email: { type: String, optional: false },
  uhID: { type: String, optional: false },
  // remaining are optional.
  desiredDegree: { type: String, optional: true },
  picture: { type: String, optional: true },
  level: { type: Number, optional: true },
  careerGoals: { type: [String], optional: true },
  interests: { type: [String], optional: true },
  website: { type: String, optional: true },
});

Template.Update_Degree_Plan_Widget.helpers({
  user() {
    if (Template.currentData().studentID.get()) {
      return Users.findDoc(Template.currentData().studentID.get());
    }
    return '';
  },
  interests() {
    return Interests.find({}, { sort: { name: 1 } });
  },
  careerGoals() {
    return CareerGoals.find({}, { sort: { name: 1 } });
  },
  desiredDegrees() {
    return DesiredDegrees.find({}, { sort: { name: 1 } });
  },
  roles() {
    return _.sortBy(_.difference(ROLES, [ROLE.ADMIN]));
  },
  slug() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return Slugs.findDoc(user.slugID).name;
    }
    return '';
  },
  semesters() {
    const currentSemester = Semesters.getCurrentSemesterDoc();
    return _.filter(Semesters.find({ sortBy: { $gte: currentSemester.sortBy } }, { sort: { sortBy: 1 } }).fetch(),
        function notSummer(s) {
          return s.term !== Semesters.SUMMER;
        });
  },
  selectedInterestIDs() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.interestIDs;
    }
    return '';
  },
  selectedCareerGoalIDs() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.careerGoalIDs;
    }
    return '';
  },
  selectedDesiredDegreeID() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.desiredDegreeID;
    }
    return '';
  },
  selectedRole() {
    if (Template.currentData().studentID.get()) {
      const user = Users.findDoc(Template.currentData().studentID.get());
      return user.roles[0];
    }
    return '';
  },
});

Template.Update_Degree_Plan_Widget.events({
  'click .jsGeneratePlan': function clickGeneratePlan(event, instance) {
    event.preventDefault();
    instance.$('.ui.modal').modal({
      onApprove() {
        const studentID = instance.data.studentID.get();
        const student = Users.findDoc(studentID);
        const currentSemester = Semesters.getCurrentSemesterDoc();
        const selectedSemesterID = instance.$('#planningSemester').val();
        let startSemester;
        if (selectedSemesterID) {
          startSemester = Semesters.findDoc(selectedSemesterID);
        } else {
          startSemester = currentSemester;
        }
        if (currentSemester.sortBy === startSemester.sortBy) {
          startSemester = semUtils.nextFallSpringSemester(startSemester);
        }
        // TODO: CAM do we really want to blow away the student's plan. What if they've made changes?
        courseUtils.clearPlannedCourseInstances(studentID);
        opportunityUtils.clearPlannedOpportunityInstances(studentID);
        const cis = CourseInstances.find({ studentID }).fetch();
        const ays = AcademicYearInstances.find({ studentID }).fetch();
        if (cis.length === 0) {
          _.map(ays, (year) => {
            AcademicYearInstances.removeIt(year._id);
          });
        } else {
          // TODO: CAM figure out which AYs to remove.
        }
        if (student.desiredDegreeID) {
          const degree = DesiredDegrees.findDoc({ _id: student.desiredDegreeID });
          if (degree.shortName.startsWith('B.S.')) {
            planUtils.generateBSDegreePlan(student, startSemester);
          }
          if (degree.shortName.startsWith('B.A.')) {
            planUtils.generateBADegreePlan(student, startSemester);
          }
        }
      },
    }).modal('show');
  },
});

Template.Update_Degree_Plan_Widget.onCreated(function updateDegreePlanWidgetOnCreated() {
  FormUtils.setupFormWidget(this, updateSchema);
  this.subscribe(AcademicYearInstances.getPublicationName());
  this.subscribe(CareerGoals.getPublicationName());
  this.subscribe(CourseInstances.getPublicationName());
  this.subscribe(Courses.getPublicationName());
  this.subscribe(DesiredDegrees.getPublicationName());
  this.subscribe(OpportunityInstances.getPublicationName());
  this.subscribe(OpportunityTypes.getPublicationName());
  this.subscribe(Opportunities.getPublicationName());
  this.subscribe(Semesters.getPublicationName());
  this.subscribe(Slugs.getPublicationName());
  this.subscribe(Interests.getPublicationName());
  this.subscribe(Users.getPublicationName());
});

Template.Update_Degree_Plan_Widget.onRendered(function updateDegreePlanWidgetOnRendered() {
  // add your statement here
});

Template.Update_Degree_Plan_Widget.onDestroyed(function updateDegreePlanWidgetOnDestroyed() {
  // add your statement here
});

<|endoftext|>


<commit_before>
var Cookie = {
    /**
    * 获取 Cookie 。
    * @param {String} name 名字。
    * @param {String} 值。
    */
    get: function (name) {
        //assert.isString(name, "Cookie.get(name): 参数 {name} ~");
        var matches = document.cookie.match(new RegExp("(?:^|; )" + encodeURIComponent(name).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1') + "=([^;]*)"));
        return matches ? decodeURIComponent(matches[1]) : null;
    },
    /**
    * 设置 Cookie 。
    * @param {String} name 名字。
    * @param {String} value 值。
    * @param {Object} options 其它属性。如 domain, path, secure， expires (单位：天) 。
    */
    set: function (name, value, options) {
        //assert.isString(name, "Cookie.get(name): 参数 {name} ~");
        var e = encodeURIComponent,
        updatedCookie = e(name) + "=" + e(value),
        expires = options && options.expires !== undefined ? options.expires : value === null ? -1 : 365;
        t = new Date();
        t.setHours(t.getHours() + expires * 24);
        updatedCookie += '; expires=' + t.toGMTString();
        for (t in options) {
            updatedCookie = updatedCookie + "; " + t + "=" + e(options[t]);
        }
        //assert(updatedCookie.length < 4096, "Cookie.set(name, value, expires, props): value 内容过长(大于 4096)，操作失败。");
        document.cookie = updatedCookie;
        return value;
    }

};
<commit_msg>修复 SetCookie null   错误<commit_after>
var Cookie = {
    /**
    * 获取 Cookie 。
    * @param {String} name 名字。
    * @param {String} 值。
    */
    get: function (name) {
        //assert.isString(name, "Cookie.get(name): 参数 {name} ~");
        var matches = document.cookie.match(new RegExp("(?:^|; )" + encodeURIComponent(name).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1') + "=([^;]*)"));
        return matches ? decodeURIComponent(matches[1]) : null;
    },
    /**
    * 设置 Cookie 。
    * @param {String} name 名字。
    * @param {String} value 值。
    * @param {Object} expires 过期时间。单位：分钟
    * @param {Object} path 路径。
    * @param {Object} domain 域名。
    * @param {Object} secure 安全限制。
    */
    set: function (name, value, expires, path, domain, secure) {
        //assert.isString(name, "Cookie.set(name): 参数 {name} ~");
        var e = encodeURIComponent,
            updatedCookie = e(name) + "=" + e(value),
            options = {path: path, domain: domain, secure: secure},
            t = new Date();
        expires = expires != undefined ? expires : value === null ? -1 : 365 * 60 * 24;
        t.setMinutes(t.getMinutes() + expires);
        updatedCookie += '; expires=' + t.toGMTString();
        for (t in options) {
            if(options[t] !== undefined) {
                updatedCookie = updatedCookie + "; " + t + "=" + e(options[t]);
            }
        }
        //assert(updatedCookie.length < 4096, "Cookie.set(name, value, expires, path, domain, secure): value 内容过长(大于 4096)，操作失败。");
        document.cookie = updatedCookie;
        return value;
    }

};
<|endoftext|>