<commit_before>function Admin() {
	//hold the tab object
	this.tab = null;

	//hold signals for the page
	this._signals = new Array();

	//hold status message for the page
	this._prompt = null;
}

/* *****	Initialization code	***** */
Admin.prototype.init = function() {
	if(!user.can_admin()) {
		status_msg('You have not been granted IDE Admin privileges', LEVEL_WARN);
		return;
	}
	if(!this._inited) {
		logDebug("Admin: Initializing");

		/* Initialize a new tab for Admin - Do this only once */
		this.tab = new Tab( "Administration" );
		this._signals.push(connect( this.tab, "onfocus", bind( this._onfocus, this ) ));
		this._signals.push(connect( this.tab, "onblur", bind( this._onblur, this ) ));
		this._signals.push(connect( this.tab, "onclickclose", bind( this._close, this ) ));
		tabbar.add_tab( this.tab );

		/* Initialise indiviual page elements */
		this.ShowTeams();
		this.GetBlogFeeds();

		/* remember that we are initialised */
		this._inited = true;
	}

	/* now switch to it */
	tabbar.switch_to(this.tab);
}
/* *****	End Initialization Code 	***** */

/* *****	Tab events: onfocus, onblur and close	***** */
Admin.prototype._onfocus = function() {
	setStyle($("admin-page"), {'display':'block'});
}

Admin.prototype._onblur = function() {
	/* Clear any prompts */
	if( this._prompt != null ) {
		this._prompt.close();
		this._prompt = null;
	}
	/* hide Admin page */
	setStyle($("admin-page"), {'display':'none'});
}

Admin.prototype._close = function() {
	/* Disconnect all signals */
	for(var i = 0; i < this._signals.length; i++) {
		disconnect(this._signals[i]);
	}
	this._signals = new Array();

	/* Close tab */
	this._onblur();
	this.tab.close();
	this._inited = false;
}
/* *****	End Tab events	***** */

/* *****	Team editing Code	***** */
Admin.prototype.ShowTeams = function() {
	var i = 0;
	var td_id = TH(null, 'Team ID');
	var td_name = TH({'class':'name'}, 'Team Name');
	var td_button = TH(null);
	var oddeven = i++ % 2 == 0 ? 'even' : 'odd';
	replaceChildNodes('admin-teams-table');
	appendChildNodes('admin-teams-table', TR({'class':oddeven}, td_id, td_name, td_button));

	for( var id in user.team_names ) {
		td_id = TD(null, id);
		td_name = TD({'class':'name'}, user.team_names[id]);
		var button = BUTTON(null, 'Edit');
		this._signals.push(connect(button, 'onclick', bind(this._editTeam, this, {id:id,name:user.team_names[id]})));
		td_button = TD(null, button);
		oddeven = i++ % 2 == 0 ? 'even' : 'odd';
		appendChildNodes('admin-teams-table',
			TR({'class':oddeven,id:'admin-teams-table-'+id}, td_id, td_name, td_button)
		);
	}
}

Admin.prototype._editTeam = function(ref) {
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	if(button.innerHTML == 'Edit') {
		var input = INPUT({value:cell.innerHTML});
		replaceChildNodes(cell, input);
		button.innerHTML = 'Save';
	} else {	//save it
		IDE_backend_request("admin/team-name-put",
			{
			   'id': ref.id,
			 'name': cell.firstChild.value
			},
			bind( this._receiveEditTeam, this, ref ),
			bind( this._errorEditTeam, this, ref )
		);
		cell.firstChild.disabled = true;
		button.disabled = true;
	}
}
Admin.prototype._receiveEditTeam = function(ref, nodes) {
	var row = $('admin-teams-table-'+ref.id);
	if(nodes.success) {
		this._prompt = status_msg("Team name updated", LEVEL_OK);
		var cell = getFirstElementByTagAndClassName('td', 'name', row);
		var button = getFirstElementByTagAndClassName('button', null, row);
		cell.innerHTML = nodes.name;
		button.innerHTML = 'Edit';
		button.disabled = false;
		user.team_names[nodes.id] = nodes.name;
		if(nodes.id == team) {
			$('teamname').innerHTML = nodes.name;
		}
	} else {
		this._errorEditTeam(ref, nodes);
	}
}
Admin.prototype._errorEditTeam = function(ref, nodes) {
	this._prompt = status_msg("Failed to update team name", LEVEL_ERROR);
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	cell.firstChild.disabled = false;
	button.disabled = false;
}
/* *****	End Team editing Code 	***** */

/* *****	Student blog feed listing code	***** */
Admin.prototype._receiveGetBlogFeeds = function(nodes) {
	var td_user = TH({'class':'user'}, 'User ID');
	var td_url = TH({'class':'url'}, 'URL');
	var td_status = TH({'class':'status'}, 'Status');
	replaceChildNodes('admin-feeds-table');
	appendChildNodes('admin-feeds-table', TR({'class':'even'}, td_user, td_url, td_status));

	var make_selectbox = function(properties, options, def) {
		var s = SELECT(properties);
		for( value in options ) {
			var opt = OPTION({value:value}, options[value]);
			if(value == def)
				opt.selected = true;
			appendChildNodes(s, opt);
		}
		return s;
	};

	var options = {unchecked:'Unchecked',valid:'Valid',invalid:'Invalid'};
	//iterate over all feeds and append them to the table
	for( var i=0; i<nodes.feeds.length; i++ ) {
		var feed = nodes.feeds[i];
		td_user = TD({'class':'user'}, feed.user);
		td_url = TD({'class':'url'}, A({href:feed.url},feed.url));

		var status = (feed.checked ? (feed.valid ? 'valid' : 'invalid') : 'unchecked');
		var selectbox = make_selectbox({id:'admin-feeds-'+feed.id}, options, status);
		var ref = {id:feed.id, url:feed.url, status:status, selectbox:selectbox};
		this._signals.push(connect(selectbox, 'onchange', bind(this.setBlogStatus, this, ref)));
		td_status = TD({'class':'status'}, selectbox);

		var oddeven = i % 2 == 0 ? 'odd' : 'even';
		appendChildNodes('admin-feeds-table', TR({'class':oddeven}, td_user, td_url, td_status));
		this.showBlogStatus(ref);
	}
}
Admin.prototype._errorGetBlogFeeds = function() {
		this._prompt = status_msg("Unable to load blog feeds", LEVEL_ERROR);
		log("Admin: Failed to retrieve feed blog urls");
		return;
}
Admin.prototype.GetBlogFeeds = function() {
	log("Admin: Retrieving blog feeds");
	IDE_backend_request("admin/feed-status-get", {},
		bind(this._receiveGetBlogFeeds, this),
		bind(this._errorGetBlogFeeds, this)
	);
}
/* *****    End Student blog feed listing code	***** */

/* *****	RSS feed validation code	***** */
Admin.prototype._receiveBlogStatus = function(ref, nodes) {
	if(nodes.success > 0 ) {
		this._prompt = status_msg("Blog feed updated", LEVEL_OK);
	} else {
		this._errorBlogStatus(ref, nodes);
	}
}
Admin.prototype._errorBlogStatus = function(ref, nodes) {
	this._prompt = status_msg("Unable to update blog feed", LEVEL_ERROR);
	$('admin-feeds-'+ref.id).value = ref.status;
	this.showBlogStatus(ref);
}
Admin.prototype.setBlogStatus = function(ref) {
	log("Admin: Setting blog feed status");
	var status = $('admin-feeds-'+ref.id).value;
	var d = loadJSONDoc("./admin/setfeedstatus", {
			id:ref.id,
			url:ref.url,
			status:status
		});
	d.addCallback( bind( this._receiveBlogStatus, this, ref) );
	d.addErrback( bind( this._errorBlogStatus, this, ref) );

	ref.status = status;
	this.showBlogStatus(ref);
}
Admin.prototype.showBlogStatus = function(ref) {
	log("Admin: Showing blog feed status");
	var tr = getFirstParentByTagAndClassName('admin-feeds-'+ref.id, 'tr', null);
	removeElementClass(tr, 'unchecked-feed-url');
	removeElementClass(tr, 'valid-feed-url');
	removeElementClass(tr, 'invalid-feed-url');
	addElementClass(tr, ref.status+'-feed-url');
}
/* *****	End RSS feed validation code	***** */
<commit_msg>Ensure that the admin page has a unique id for each feed<commit_after>function Admin() {
	//hold the tab object
	this.tab = null;

	//hold signals for the page
	this._signals = new Array();

	//hold status message for the page
	this._prompt = null;
}

/* *****	Initialization code	***** */
Admin.prototype.init = function() {
	if(!user.can_admin()) {
		status_msg('You have not been granted IDE Admin privileges', LEVEL_WARN);
		return;
	}
	if(!this._inited) {
		logDebug("Admin: Initializing");

		/* Initialize a new tab for Admin - Do this only once */
		this.tab = new Tab( "Administration" );
		this._signals.push(connect( this.tab, "onfocus", bind( this._onfocus, this ) ));
		this._signals.push(connect( this.tab, "onblur", bind( this._onblur, this ) ));
		this._signals.push(connect( this.tab, "onclickclose", bind( this._close, this ) ));
		tabbar.add_tab( this.tab );

		/* Initialise indiviual page elements */
		this.ShowTeams();
		this.GetBlogFeeds();

		/* remember that we are initialised */
		this._inited = true;
	}

	/* now switch to it */
	tabbar.switch_to(this.tab);
}
/* *****	End Initialization Code 	***** */

/* *****	Tab events: onfocus, onblur and close	***** */
Admin.prototype._onfocus = function() {
	setStyle($("admin-page"), {'display':'block'});
}

Admin.prototype._onblur = function() {
	/* Clear any prompts */
	if( this._prompt != null ) {
		this._prompt.close();
		this._prompt = null;
	}
	/* hide Admin page */
	setStyle($("admin-page"), {'display':'none'});
}

Admin.prototype._close = function() {
	/* Disconnect all signals */
	for(var i = 0; i < this._signals.length; i++) {
		disconnect(this._signals[i]);
	}
	this._signals = new Array();

	/* Close tab */
	this._onblur();
	this.tab.close();
	this._inited = false;
}
/* *****	End Tab events	***** */

/* *****	Team editing Code	***** */
Admin.prototype.ShowTeams = function() {
	var i = 0;
	var td_id = TH(null, 'Team ID');
	var td_name = TH({'class':'name'}, 'Team Name');
	var td_button = TH(null);
	var oddeven = i++ % 2 == 0 ? 'even' : 'odd';
	replaceChildNodes('admin-teams-table');
	appendChildNodes('admin-teams-table', TR({'class':oddeven}, td_id, td_name, td_button));

	for( var id in user.team_names ) {
		td_id = TD(null, id);
		td_name = TD({'class':'name'}, user.team_names[id]);
		var button = BUTTON(null, 'Edit');
		this._signals.push(connect(button, 'onclick', bind(this._editTeam, this, {id:id,name:user.team_names[id]})));
		td_button = TD(null, button);
		oddeven = i++ % 2 == 0 ? 'even' : 'odd';
		appendChildNodes('admin-teams-table',
			TR({'class':oddeven,id:'admin-teams-table-'+id}, td_id, td_name, td_button)
		);
	}
}

Admin.prototype._editTeam = function(ref) {
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	if(button.innerHTML == 'Edit') {
		var input = INPUT({value:cell.innerHTML});
		replaceChildNodes(cell, input);
		button.innerHTML = 'Save';
	} else {	//save it
		IDE_backend_request("admin/team-name-put",
			{
			   'id': ref.id,
			 'name': cell.firstChild.value
			},
			bind( this._receiveEditTeam, this, ref ),
			bind( this._errorEditTeam, this, ref )
		);
		cell.firstChild.disabled = true;
		button.disabled = true;
	}
}
Admin.prototype._receiveEditTeam = function(ref, nodes) {
	var row = $('admin-teams-table-'+ref.id);
	if(nodes.success) {
		this._prompt = status_msg("Team name updated", LEVEL_OK);
		var cell = getFirstElementByTagAndClassName('td', 'name', row);
		var button = getFirstElementByTagAndClassName('button', null, row);
		cell.innerHTML = nodes.name;
		button.innerHTML = 'Edit';
		button.disabled = false;
		user.team_names[nodes.id] = nodes.name;
		if(nodes.id == team) {
			$('teamname').innerHTML = nodes.name;
		}
	} else {
		this._errorEditTeam(ref, nodes);
	}
}
Admin.prototype._errorEditTeam = function(ref, nodes) {
	this._prompt = status_msg("Failed to update team name", LEVEL_ERROR);
	var row = $('admin-teams-table-'+ref.id);
	var cell = getFirstElementByTagAndClassName('td', 'name', row);
	var button = getFirstElementByTagAndClassName('button', null, row);
	cell.firstChild.disabled = false;
	button.disabled = false;
}
/* *****	End Team editing Code 	***** */

/* *****	Student blog feed listing code	***** */
Admin.prototype._receiveGetBlogFeeds = function(nodes) {
	var td_user = TH({'class':'user'}, 'User ID');
	var td_url = TH({'class':'url'}, 'URL');
	var td_status = TH({'class':'status'}, 'Status');
	replaceChildNodes('admin-feeds-table');
	appendChildNodes('admin-feeds-table', TR({'class':'even'}, td_user, td_url, td_status));

	var make_selectbox = function(properties, options, def) {
		var s = SELECT(properties);
		for( value in options ) {
			var opt = OPTION({value:value}, options[value]);
			if(value == def)
				opt.selected = true;
			appendChildNodes(s, opt);
		}
		return s;
	};

	var options = {unchecked:'Unchecked',valid:'Valid',invalid:'Invalid'};
	//iterate over all feeds and append them to the table
	for( var i=0; i<nodes.feeds.length; i++ ) {
		// Add an id to each field to ensure that they're unique.
		// These used to be the DB ids, but there's no DB any more...
		nodes.feeds[i].id = i;
		var feed = nodes.feeds[i];
		td_user = TD({'class':'user'}, feed.user);
		td_url = TD({'class':'url'}, A({href:feed.url},feed.url));

		var status = (feed.checked ? (feed.valid ? 'valid' : 'invalid') : 'unchecked');
		var selectbox = make_selectbox({id:'admin-feeds-'+feed.id}, options, status);
		var ref = {id:feed.id, url:feed.url, status:status, selectbox:selectbox};
		this._signals.push(connect(selectbox, 'onchange', bind(this.setBlogStatus, this, ref)));
		td_status = TD({'class':'status'}, selectbox);

		var oddeven = i % 2 == 0 ? 'odd' : 'even';
		appendChildNodes('admin-feeds-table', TR({'class':oddeven}, td_user, td_url, td_status));
		this.showBlogStatus(ref);
	}
}
Admin.prototype._errorGetBlogFeeds = function() {
		this._prompt = status_msg("Unable to load blog feeds", LEVEL_ERROR);
		log("Admin: Failed to retrieve feed blog urls");
		return;
}
Admin.prototype.GetBlogFeeds = function() {
	log("Admin: Retrieving blog feeds");
	IDE_backend_request("admin/feed-status-get", {},
		bind(this._receiveGetBlogFeeds, this),
		bind(this._errorGetBlogFeeds, this)
	);
}
/* *****    End Student blog feed listing code	***** */

/* *****	RSS feed validation code	***** */
Admin.prototype._receiveBlogStatus = function(ref, nodes) {
	if(nodes.success > 0 ) {
		this._prompt = status_msg("Blog feed updated", LEVEL_OK);
	} else {
		this._errorBlogStatus(ref, nodes);
	}
}
Admin.prototype._errorBlogStatus = function(ref, nodes) {
	this._prompt = status_msg("Unable to update blog feed", LEVEL_ERROR);
	$('admin-feeds-'+ref.id).value = ref.status;
	this.showBlogStatus(ref);
}
Admin.prototype.setBlogStatus = function(ref) {
	log("Admin: Setting blog feed status");
	var status = $('admin-feeds-'+ref.id).value;
	var d = loadJSONDoc("./admin/setfeedstatus", {
			id:ref.id,
			url:ref.url,
			status:status
		});
	d.addCallback( bind( this._receiveBlogStatus, this, ref) );
	d.addErrback( bind( this._errorBlogStatus, this, ref) );

	ref.status = status;
	this.showBlogStatus(ref);
}
Admin.prototype.showBlogStatus = function(ref) {
	log("Admin: Showing blog feed status");
	var tr = getFirstParentByTagAndClassName('admin-feeds-'+ref.id, 'tr', null);
	removeElementClass(tr, 'unchecked-feed-url');
	removeElementClass(tr, 'valid-feed-url');
	removeElementClass(tr, 'invalid-feed-url');
	addElementClass(tr, ref.status+'-feed-url');
}
/* *****	End RSS feed validation code	***** */
<|endoftext|>


<commit_before>"use strict";
var URL = require("url");
var headersKeys = "Content-Type,Content-Length,User-Agent,Accept-Language,Accept-Encoding,Range,If-Range,Last-Modified".split(",");
function cross(req, res, referer) {
    if (referer) {
        var { pathname, search } = URL.parse(referer);
    } else {
        var { pathname, search } = URL.parse(req.url);
    }
    var slice_end = pathname.indexOf("@");
    if (slice_end < 0) {
        slice_end = pathname.length;
    }
    var jsondata = decodeURIComponent(pathname.slice(1, slice_end));
    var realpath = referer ? req.url.slice(1) : pathname.slice(slice_end + 1) + (search || "");
    if (referer) {
        var redirect = "/" + encodeURIComponent(jsondata) + "@" + realpath;
        res.writeHead(302, {
            "Location": redirect
        });
        return res.end();
    }
    try {
        var $cross = JSON.parse(jsondata);
        if (!$cross.token) throw new Error("验证身份失败！");
        var
            $url = encodeURI($cross['url'] + realpath),
            // $data = $cross['data'],//不再接受数据参数，如果是get请直接写入$url，如果是post，请直接post
            $method = $cross['method'] || req.method,//$_SERVER['REQUEST_METHOD'];
            $headers = $cross['headers'] || {};
        var _headers = req.headers;
        headersKeys.forEach(function (key) {
            if (!$headers[key] && _headers[key]) {
                $headers[key] = _headers[key];
            }
        });
        var http;
        if (/^https\:/i.test($url)) {
            http = require("https");
        } else {
            http = require("http");
        }
        var request = http.request(Object.assign({
            method: $method,
            headers: $headers,
            rejectUnauthorized: false// 放行证书不可用的网站
        }, URL.parse($url)), function (response) {
            var headers = response.headers;
            headers.origin && (headers["Access-Control-Allow-Credentials"] = true);
            var setCookie = headers["set-cookie"];
            if (setCookie) headers["cross-cookie"] = setCookie, delete headers["set-cookie"];
            if (headers.location) {
                headers["cross-location"] = headers.location;
                delete headers.location;
            }
            res.writeHead(response.statusCode, headers);
            response.pipe(res);
        });
        request.setTimeout(36000/*support for wechat long pull*/);
        request.on("error", function (e) {
            res.writeHead(403, {});
            res.end(String(e));
        });
        req.pipe(request);
    } catch (e) {
        res.writeHead(403, {});
        res.end(String(e));
    }
}
module.exports = cross;<commit_msg>增加一种数据格式<commit_after>"use strict";
var URL = require("url");
var headersKeys = "Content-Type,Content-Length,User-Agent,Accept-Language,Accept-Encoding,Range,If-Range,Last-Modified".split(",");
function cross(req, res, referer) {
    if (referer) {
        var { pathname, search } = URL.parse(referer);
    } else {
        var { pathname, search } = URL.parse(req.url);
    }
    var slice_end = pathname.indexOf("@");
    if (slice_end < 0) {
        slice_end = pathname.length;
    }
    var jsonlike = decodeURIComponent(pathname.slice(1, slice_end));
    var realpath = referer ? req.url.slice(1) : pathname.slice(slice_end + 1) + (search || "");
    if (referer) {
        var redirect = "/" + encodeURIComponent(jsonlike) + "@" + realpath;
        res.writeHead(302, {
            "Location": redirect
        });
        return res.end();
    }
    try {
        var matchlike = /^\{(s?)\/\/(.*?)\/(.*?)\}$/i.exec(jsonlike);
        if (matchlike) {
            var $cross = {};
            // {s//wx2.qq.com/k=v,k=v,k=v}
            let headers = $cross.headers = {};
            let [, s, host, header] = matchlike;
            $cross.url = `http${s}://${host}/`;
            header.split("&").forEach(function (kv) {
                var [k, v] = kv.split("=");
                if (k && v) headers[decodeURIComponent(k)] = decodeURIComponent(v);
            });
        } else {
            var $cross = JSON.parse(jsonlike);
            if (!$cross.token) throw new Error("验证身份失败！");
        }
        var
            $url = encodeURI($cross['url'] + realpath),
            // $data = $cross['data'],//不再接受数据参数，如果是get请直接写入$url，如果是post，请直接post
            $method = $cross['method'] || req.method,//$_SERVER['REQUEST_METHOD'];
            $headers = $cross['headers'] || {};
        var _headers = req.headers;
        headersKeys.forEach(function (key) {
            if (!$headers[key] && _headers[key]) {
                $headers[key] = _headers[key];
            }
        });
        var http;
        if (/^https\:/i.test($url)) {
            http = require("https");
        } else {
            http = require("http");
        }
        var request = http.request(Object.assign({
            method: $method,
            headers: $headers,
            rejectUnauthorized: false// 放行证书不可用的网站
        }, URL.parse($url)), function (response) {
            var headers = response.headers;
            headers.origin && (headers["Access-Control-Allow-Credentials"] = true);
            var setCookie = headers["set-cookie"];
            if (setCookie) headers["cross-cookie"] = setCookie, delete headers["set-cookie"];
            if (headers.location) {
                headers["cross-location"] = headers.location;
                delete headers.location;
            }
            res.writeHead(response.statusCode, headers);
            response.pipe(res);
        });
        request.setTimeout(36000/*support for wechat long pull*/);
        request.on("error", function (e) {
            res.writeHead(403, {});
            res.end(String(e));
        });
        req.pipe(request);
    } catch (e) {
        res.writeHead(403, {});
        res.end(String(e));
    }
}
module.exports = cross;<|endoftext|>


<commit_before>'use strict';

var d        = require('es5-ext/lib/Object/descriptor')
  , Db       = require('dbjs')
  , relation = require('dbjs/lib/_relation');

module.exports = Object.defineProperties(relation, {
	toDOMInput: d(function (document/*, options*/) {
		var input, options, ns, required, onChange, multiple;
		options = Object(arguments[1]);
		ns = this.__ns.__value;
		options.relation = this;
		required = this.__required.__value;
		if (required) options.required = true;
		multiple = options.multiple = this.__multiple.__value;
		if (options.name == null) options.name = this._id_;
		input = ns.toDOMInput(document, options);
		input.value = this.objectValue;
		if (required) {
			input.required = required;
			input.valid = (input.value != null);
		}
		if (options.disabled) input.castAttribute('disabled', true);
		onChange = function () { input.value = this.objectValue; };
		if (multiple) {
			this.on('add', onChange);
			this.on('delete', onChange);
		} else {
			this.on('change', onChange);
		}
		return input;
	}),
	DOMId: d.gs(function () {
		return this._id_.replace(/:/g, '-').replace('#', '-');
	})
});

relation.get('fieldHint').ns = Db.String;
<commit_msg>Do not set required if already preset<commit_after>'use strict';

var d        = require('es5-ext/lib/Object/descriptor')
  , Db       = require('dbjs')
  , relation = require('dbjs/lib/_relation');

module.exports = Object.defineProperties(relation, {
	toDOMInput: d(function (document/*, options*/) {
		var input, options, ns, required, onChange, multiple;
		options = Object(arguments[1]);
		ns = this.__ns.__value;
		options.relation = this;
		required = this.__required.__value;
		if (required && (options.required == null)) options.required = true;
		multiple = options.multiple = this.__multiple.__value;
		if (options.name == null) options.name = this._id_;
		input = ns.toDOMInput(document, options);
		input.value = this.objectValue;
		if (required) {
			input.required = required;
			input.valid = (input.value != null);
		}
		if (options.disabled) input.castAttribute('disabled', true);
		onChange = function () { input.value = this.objectValue; };
		if (multiple) {
			this.on('add', onChange);
			this.on('delete', onChange);
		} else {
			this.on('change', onChange);
		}
		return input;
	}),
	DOMId: d.gs(function () {
		return this._id_.replace(/:/g, '-').replace('#', '-');
	})
});

relation.get('fieldHint').ns = Db.String;
<|endoftext|>


<commit_before>var request = require('request');
var oauth = require('oauth');
var sentiment = require('sentiment');

var oauth = new oauth.OAuth(
  'https://api.twitter.com/oauth/request_token',
  'https://api.twitter.com/oauth/access_token',
  process.env.CONSUMER_KEY,
  process.env.CONSUMER_SECRET,
  '1.0A',
  null,
  'HMAC-SHA1'
);

exports.getMentions = function(req, res, callback){
  callback = callback || analyzeTweets;
  oauth.get(
    'https://api.twitter.com/1.1/statuses/mentions_timeline.json?count=800',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    function (e, data, res){
      if (e) return console.error(e);
      callback(data);
      // res.send(200,'tweets obtained!')
    });
}

exports.sendMessage = function(message, number){
  console.log('sending message!')
  number = number || process.env.TEST_PHONE;
  var text = {
    api_key: process.env.NEXMO_KEY,
    api_secret: process.env.NEXMO_SECRET,
    from: process.env.NEXMO_PHONE,
    to: number,
    text: message
  };

  request.post('https://rest.nexmo.com/sms/json',{
    form: text
  },function(err,data){
    if(err) return console.error(err);
    console.log('DATA!',data.body);
  });
};

exports.callWit = function(req, res){
  var headers = {
    Authorization: 'Bearer ' + process.env.WITAI_KEY,
  };

  console.log(req.query.text);

  var options = {
    url: 'https://api.wit.ai/message',
    headers: headers,
    form: {q: req.query.text, v: '20140528'}
  };

  request.get(options, function(err, dummyVariable, body){
    if(err) return console.error(err);
    body = JSON.parse(body);
    var intent = body.outcome.intent;
    console.log(intent);
    switch (intent) {
      case 'eventSummary':
        // var startTime, endTime;
        // if (body.outcome.entities.datetime) {
        //   if (Array.isArray(body.outcome.entities.datetime)) {
        //     startTime = body.outcome.entities.datetime[0].value.from;
        //     endTime = body.outcome.entities.datetime[1].value.from;
        //   } else {
        //     startTime = body.outcome.entities.datetime[0].value.from;
        //   }
        // }
        exports.getMentions(req, res, function(tweets) {
          var score = analyzeTweets(tweets);
          console.log(score);
          exports.sendMessage('Your self-worth is: ' + score + ' ^ _ ^ ');
        });
        break;
      case 'bestFeedback':
      console.log('bestFeedback');
        findLove();
        break;
      case 'worstIssue':
      console.log('worstIssue');
        findHate();
        break;
      case 'scheduleTweet':
      console.log('scheduleTweet');
        var time;
        if (body.outcome.entities.datetime) {
          time = body.outcome.entities.datetime.value.from;
        }
        var message = body.outcome.entities.message_body.value;
        scheduleTweet(time, message);
        break;
      default:
        console.log('oops');
    }
  });
};

var getSummary = function(startTime, endTime){
  retrieve
  var tweets = retrieveTweets(startTime, endTime);
  var compositeScore = analyzeTweets(tweets);
  exports.sendMessage('The average attendee rates this event at ' + compositeScore + '.');
};

var findLove = function(){
 exports.sendMessage(app.get('love').pop().tweet, process.env.TEST_PHONE);
};

var findHate = function(){
 exports.sendMessage(app.get('hate').pop().tweet, process.env.TEST_PHONE);
};

var scheduleTweet = function(time, tweet){
 if(!time){
   time = new Date();
 }
 var tweetAt = new Date(time);
 var now = new Date();
 var wait = tweetAt - now;
 setTimeout(function(){
   exports.sendTweet(tweet);
 }, wait);
};

var sentimentAnalysis = function(text){
 // returns sentiment score for one message
 console.log('sentiment!!!!', sentiment(text).score);
 return 666;
};

var loveThreshold = 600;
var hateThreshold = -600;
var love = [];
var hate = [];
var compositeScore = 0;

var analyzeTweets = function(tweets){
  // tweets is array with tweet objects
  // analyzeTweets iterates over all tweets,
  tweets = JSON.parse(tweets);
  compositeScore = 0;
  for (var i = 0; i < tweets.length; i++){
    // calling sentimentAnalysis on each,
    var score = sentimentAnalysis(tweets[i].text);
    if(score > loveThreshold){
      // add to love
      love.push(tweets[i]);
      // retweet
      retweet(tweets[i].id_str);
    } else if(score < hateThreshold){
      // add to hate
      hate.push(tweets[i]);
      // text to organizer
      setTimeout(function(){exports.sendMessage(tweets[i].text)},1000*i);
    }
    compositeScore += score;
  }
  compositeScore /= i;
  // then calculates average sentiment score

  return compositeScore;
};

var retweet = function(tweet){
  console.log('retweeting', tweet);
  oauth.post(
    'https://api.twitter.com/1.1/statuses/retweet/' + tweet + '.json',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    null,
    null,
    function (e, data,res){
      console.log('callaback!')
      if (e) return console.error(e);
    });
}

var retrieveTweets = function(req, res, callback){
  callback = callback || analyzeTweets;
  oauth.get(
    'https://api.twitter.com/1.1/statuses/mentions_timeline.json?count=800',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    function (e, data, res){
      if (e) return console.error(e);
      callback(data);
      // res.send(200,'tweets obtained!')
    });
};

exports.sendTweet = function(tweet){
 // post tweet...
  oauth.post(
    'https://api.twitter.com/1.1/statuses/update.json',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    {status:tweet},
    'text/html',
    function (e, data,res){
      if (e) return console.error(e);
    });
};
<commit_msg>Set sentiment threshold. We're sensitive now<commit_after>var request = require('request');
var oauth = require('oauth');
var sentiment = require('sentiment');

var oauth = new oauth.OAuth(
  'https://api.twitter.com/oauth/request_token',
  'https://api.twitter.com/oauth/access_token',
  process.env.CONSUMER_KEY,
  process.env.CONSUMER_SECRET,
  '1.0A',
  null,
  'HMAC-SHA1'
);

exports.getMentions = function(req, res, callback){
  callback = callback || analyzeTweets;
  oauth.get(
    'https://api.twitter.com/1.1/statuses/mentions_timeline.json?count=800',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    function (e, data, res){
      if (e) return console.error(e);
      callback(data);
      // res.send(200,'tweets obtained!')
    });
}

exports.sendMessage = function(message, number){
  console.log('sending message!')
  number = number || process.env.TEST_PHONE;
  var text = {
    api_key: process.env.NEXMO_KEY,
    api_secret: process.env.NEXMO_SECRET,
    from: process.env.NEXMO_PHONE,
    to: number,
    text: message
  };

  request.post('https://rest.nexmo.com/sms/json',{
    form: text
  },function(err,data){
    if(err) return console.error(err);
    console.log('DATA!',data.body);
  });
};

exports.callWit = function(req, res){
  var headers = {
    Authorization: 'Bearer ' + process.env.WITAI_KEY,
  };

  console.log(req.query.text);

  var options = {
    url: 'https://api.wit.ai/message',
    headers: headers,
    form: {q: req.query.text, v: '20140528'}
  };

  request.get(options, function(err, dummyVariable, body){
    if(err) return console.error(err);
    body = JSON.parse(body);
    var intent = body.outcome.intent;
    console.log(intent);
    switch (intent) {
      case 'eventSummary':
        // var startTime, endTime;
        // if (body.outcome.entities.datetime) {
        //   if (Array.isArray(body.outcome.entities.datetime)) {
        //     startTime = body.outcome.entities.datetime[0].value.from;
        //     endTime = body.outcome.entities.datetime[1].value.from;
        //   } else {
        //     startTime = body.outcome.entities.datetime[0].value.from;
        //   }
        // }
        exports.getMentions(req, res, function(tweets) {
          var score = analyzeTweets(tweets);
          console.log(score);
          exports.sendMessage('Your self-worth is: ' + score + ' ^ _ ^ ');
        });
        break;
      case 'bestFeedback':
      console.log('bestFeedback');
        findLove();
        break;
      case 'worstIssue':
      console.log('worstIssue');
        findHate();
        break;
      case 'scheduleTweet':
      console.log('scheduleTweet');
        var time;
        if (body.outcome.entities.datetime) {
          time = body.outcome.entities.datetime.value.from;
        }
        var message = body.outcome.entities.message_body.value;
        scheduleTweet(time, message);
        break;
      default:
        console.log('oops');
    }
  });
};

var getSummary = function(startTime, endTime){
  retrieve
  var tweets = retrieveTweets(startTime, endTime);
  var compositeScore = analyzeTweets(tweets);
  exports.sendMessage('The average attendee rates this event at ' + compositeScore + '.');
};

var findLove = function(){
 exports.sendMessage(app.get('love').pop().tweet, process.env.TEST_PHONE);
};

var findHate = function(){
 exports.sendMessage(app.get('hate').pop().tweet, process.env.TEST_PHONE);
};

var scheduleTweet = function(time, tweet){
 if(!time){
   time = new Date();
 }
 var tweetAt = new Date(time);
 var now = new Date();
 var wait = tweetAt - now;
 setTimeout(function(){
   exports.sendTweet(tweet);
 }, wait);
};

var sentimentAnalysis = function(text){
 // returns sentiment score for one message
 return sentiment(text).score;
};

var loveThreshold = 5;
var hateThreshold = -5;
var love = [];
var hate = [];

var analyzeTweets = function(tweets){
  // tweets is array with tweet objects
  // analyzeTweets iterates over all tweets,
  tweets = JSON.parse(tweets);
  compositeScore = 0;
  for (var i = 0; i < tweets.length; i++){
    // calling sentimentAnalysis on each,
    var score = sentimentAnalysis(tweets[i].text);
    if(score > loveThreshold){
      // add to love
      love.push(tweets[i]);
      // retweet
      retweet(tweets[i].id_str);
    } else if(score < hateThreshold){
      // add to hate
      hate.push(tweets[i]);
      // text to organizer
      setTimeout(function(){exports.sendMessage(tweets[i].text)},1000*i);
    }
    compositeScore += score;
  }
  compositeScore /= i;
  // then calculates average sentiment score

  return compositeScore;
};

var retweet = function(tweet){
  console.log('retweeting', tweet);
  oauth.post(
    'https://api.twitter.com/1.1/statuses/retweet/' + tweet + '.json',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    null,
    null,
    function (e, data,res){
      console.log('callaback!')
      if (e) return console.error(e);
    });
}

var retrieveTweets = function(req, res, callback){
  callback = callback || analyzeTweets;
  oauth.get(
    'https://api.twitter.com/1.1/statuses/mentions_timeline.json?count=800',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    function (e, data, res){
      if (e) return console.error(e);
      callback(data);
      // res.send(200,'tweets obtained!')
    });
};

exports.sendTweet = function(tweet){
 // post tweet...
  oauth.post(
    'https://api.twitter.com/1.1/statuses/update.json',
    process.env.ACCESS_TOKEN_KEY, //test user token
    process.env.ACCESS_TOKEN_SECRET, //test user secret
    {status:tweet},
    'text/html',
    function (e, data,res){
      if (e) return console.error(e);
    });
};
<|endoftext|>


<commit_before>var EventEmitter = require('events');
var BotKit = require('botkit');

NEWSCHEMA('Bot').make(function(schema) {
    schema.define('parser', 'Object');
    schema.define('slack', 'Object');
    schema.define('emitter', 'Object');
    schema.define('bot', 'Object');

    schema.setDefault(function(name) {
        switch(name) {
            case 'parser':
                return GETSCHEMA('Parser');
            case 'emitter':
                return new EventEmitter();
        }
    });

    /**
     * Connect slackbot and initialize real time messaging
     * @return EMIT MESSAGE
     */
    schema.addWorkflow('connect', function(error, model, options, callback) {

        model.slack = BotKit.slackbot({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ['bot'],
        });
        model.bot = model.slack.spawn({
            token: process.env.SLACK_API_KEY
        }).startRTM();

        model.slack.setupWebserver(process.env.PORT || 3000, function(err,webserver) {
            model.slack.createWebhookEndpoints(model.slack.webserver);

            model.slack.createOauthEndpoints(model.slack.webserver, function(err,req,res) {
                // if (err) {
                //     res.status(500).send('ERROR: ' + err);
                // } else {
                //     res.send('Success!');
                // }
            });
        });

        model.parser.operation('initAnalyzer');

        model.slack.on('interactive_message_callback', function(bot, message) {
            console.log('RECEIVED MESSAGE', message);
        });

        model.slack.on('direct_message', function(bot, message) {
            model.parser.operation('parseMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    model.emitter.emit(intent.module, intent);
                }
            });
        });

        model.slack.on('direct_mention', function(bot,message) {
            model.parser.operation('parseMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    model.emitter.emit(intent.module, intent);
                }
            });
        });
        return callback();

    });

    schema.addWorkflow('reply', function(error, model, options, callback) {
        console.log('ODOSLANE');
        model.bot.reply(options.message, options.response);
        return callback();
    });

    schema.addWorkflow('askQuestion', function(error, model, options, callback) {
        let conversation = options.conversation;
        let text = options.question;
        conversation.ask(text, function(message, conversation) {
            model.parser.operation('parseStandupMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    intent.conversation = conversation;
                    model.emitter.emit(intent.module, intent);
                }
            });
        });
    });

    schema.addWorkflow('postMessage', function(error, model, message, callback) {
        model.bot.say(message);
        return callback();
    });

    schema.addWorkflow('startConversations', function(error, model, options, callback) {
        var users = options.users;
        users.forEach(function(user) {
            model.bot.startPrivateConversation( { user: user.slackID }, function(err, conversation) {
                console.log('PRIVATE CONVERSATION STARTED WITH', user.slackID);
                return callback(err, conversation);
            });
        });
    });

    schema.addWorkflow('getMembers', function(error, model, options, callback) {
        model.bot.api.users.list({},function(err, responseMembers) {
            if (err) {
                return callback(err);
            }
            return callback(err, responseMembers);
        });
    });
});
<commit_msg>test 8<commit_after>var EventEmitter = require('events');
var BotKit = require('botkit');

NEWSCHEMA('Bot').make(function(schema) {
    schema.define('parser', 'Object');
    schema.define('slack', 'Object');
    schema.define('emitter', 'Object');
    schema.define('bot', 'Object');

    schema.setDefault(function(name) {
        switch(name) {
            case 'parser':
                return GETSCHEMA('Parser');
            case 'emitter':
                return new EventEmitter();
        }
    });

    /**
     * Connect slackbot and initialize real time messaging
     * @return EMIT MESSAGE
     */
    schema.addWorkflow('connect', function(error, model, options, callback) {

        model.slack = BotKit.slackbot({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ['bot'],
        });
        model.bot = model.slack.spawn({
            token: process.env.SLACK_API_KEY
        }).startRTM();

        model.slack.setupWebserver(3000, function(err,webserver) {
            model.slack.createWebhookEndpoints(model.slack.webserver);

            model.slack.createOauthEndpoints(model.slack.webserver, function(err,req,res) {
                // if (err) {
                //     res.status(500).send('ERROR: ' + err);
                // } else {
                //     res.send('Success!');
                // }
            });
        });

        model.parser.operation('initAnalyzer');

        model.slack.on('interactive_message_callback', function(bot, message) {
            console.log('RECEIVED MESSAGE', message);
        });

        model.slack.on('direct_message', function(bot, message) {
            model.parser.operation('parseMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    model.emitter.emit(intent.module, intent);
                }
            });
        });

        model.slack.on('direct_mention', function(bot,message) {
            model.parser.operation('parseMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    model.emitter.emit(intent.module, intent);
                }
            });
        });
        return callback();

    });

    schema.addWorkflow('reply', function(error, model, options, callback) {
        console.log('ODOSLANE');
        model.bot.reply(options.message, options.response);
        return callback();
    });

    schema.addWorkflow('askQuestion', function(error, model, options, callback) {
        let conversation = options.conversation;
        let text = options.question;
        conversation.ask(text, function(message, conversation) {
            model.parser.operation('parseStandupMessage', message, function(err, intent) {
                if (err) {
                    console.log('ERR PARSER: ', err);
                    return;
                }
                if (intent) {
                    intent.conversation = conversation;
                    model.emitter.emit(intent.module, intent);
                }
            });
        });
    });

    schema.addWorkflow('postMessage', function(error, model, message, callback) {
        model.bot.say(message);
        return callback();
    });

    schema.addWorkflow('startConversations', function(error, model, options, callback) {
        var users = options.users;
        users.forEach(function(user) {
            model.bot.startPrivateConversation( { user: user.slackID }, function(err, conversation) {
                console.log('PRIVATE CONVERSATION STARTED WITH', user.slackID);
                return callback(err, conversation);
            });
        });
    });

    schema.addWorkflow('getMembers', function(error, model, options, callback) {
        model.bot.api.users.list({},function(err, responseMembers) {
            if (err) {
                return callback(err);
            }
            return callback(err, responseMembers);
        });
    });
});
<|endoftext|>


<commit_before>'use strict';
/* global require */
var crypto              =   require('crypto'),
    fs                  =   require('fs'),
    mime                =   require('mime'),
    request             =   require('request'),
    s3                  =   require('s3'),
    Answer              =   require('mongoose').model('Answer'),
    Doc                 =   require('mongoose').model('Documents'),
    FileUploadStatus    =   require('mongoose').model('FileUploadStatus'),
    phantom             =   require('phantom'),
    //MendeleyToken     =   require('mongoose').model('MendeleyToken'),
    upload_bucket       =   process.env.DOC_BUCKET,
    client              =   s3.createClient({
                                maxAsyncS3: 20,     // this is the default
                                s3RetryCount: 3,    // this is the default
                                s3RetryDelay: 1000, // this is the default
                                multipartUploadThreshold: 20971520, // this is the default (20 MB)
                                multipartUploadSize: 15728640, // this is the default (15 MB)
                                s3Options: {
                                    accessKeyId: process.env.DOC_AWS_ACCESS_KEY,
                                    secretAccessKey: process.env.DOC_AWS_SECRET_KEY
                                    // any other options are passed to new AWS.S3()
                                    // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property
                                }
                            });

var uploadFile = function(file, req, callback) {
    var hash = crypto.createHash('sha1'),
        timestamp = new Date().toISOString(),
        file_extension = getFileExtension(file.path);

    fs.readFile(file.path, {encoding: 'utf8'}, function (err, data) {
        hash.update(data);
        // get hashed value of file as fingerprint
        var file_hash = hash.digest('hex');
        // search documents for hashed file
        Doc.findOne({file_hash: file_hash}, function (err, document) {
            // if file exists tag for reference
            if (document !== null) {
                callback(null, document);
                // if not upload to s3 with hashed value as file name,
                // create record with hash value and end url
            } else {
                // upload parameters
                var params = {
                    localFile: file.path,
                    s3Params: {
                        Bucket: upload_bucket,
                        Key: String(file_hash) + '.' + file_extension
                        // other options supported by putObject, except Body and ContentLength.
                        // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
                    }
                };

                var uploader = client.uploadFile(params);
                uploader.on('error', function(err) {
                    console.error("unable to upload:", err.stack);
                });
                uploader.on('progress', function() {
                    console.log("progress", uploader.progressMd5Amount, uploader.progressAmount, uploader.progressTotal);
                });
                uploader.on('end', function() {
                    console.log("done uploading");
                });

                Doc.create({
                    file_hash: file_hash,
                    mime_type: file.type,
                    s3_url: 'https://s3.amazonaws.com/' + upload_bucket + '/' + file_hash + '.' + file_extension,
                    modified: [{
                        modified_by: req.user._id,
                        modified_date: timestamp
                    }],
                    createdBy: req.user._id,
                    creationDate: timestamp
                }, callback);
            }
        });
    });
};

var getFileExtension = function(filePath) {
    return filePath.split('.')[filePath.split('.').length - 1];
};

var getFileName = function(timestamp, user, extension) {
    return user + '-' + timestamp + (extension ? ('.' + extension) : '');
};

exports.getRemoteFileUploadStatus = function (req, res) {
    FileUploadStatus.findOne({_id: req.params.statusId}, function(err, uploadStatus) {
        res.send(uploadStatus);
    });
};

exports.uploadUrlSnapshot = function(req, res) {
    var width = 1024, height = 768,
        terminateWithError = function(errorCode, ph) {
            res.send({error: errorCode});
            console.log(errorCode);

            if(ph) {
                ph.exit();
            }
        };

    phantom.create().then(function(ph) {
        ph.createPage().then(function(page) {
            page.property('viewportSize', {width: width, height: height}).then(function () {
                var timeout = setTimeout(function() {
                    terminateWithError('PAGE_LOADING_TIMEOUT_EXPIRED', ph);
                }, 30000);

                page.open(req.query.url).then(function () {
                    clearTimeout(timeout);

                    page.evaluate(function () {
                        return document.body.offsetHeight;
                    }).then(function (actualHeight) {
                        if (actualHeight > 3000) {
                            terminateWithError('TOO_LARGE_SIZE', ph);
                        } else {
                            page.property('viewportSize', {width: width, height: actualHeight}).then(function () {
                                page.open(req.query.url).then(function () {
                                    var filePath = '/tmp/' + getFileName(new Date().getTime(), req.user._id, 'png');
                                    page.render(filePath);

                                    var interval = setInterval(function() {
                                        fs.exists(filePath, function(renderingCompleted) {
                                            if (renderingCompleted) {
                                                uploadFile({path: filePath, type: mime.lookup(filePath)}, req,
                                                    function (errorUpload, doc) {
                                                        fs.unlink(filePath);

                                                        if (errorUpload) {
                                                            terminateWithError('S3_TRANSFER_FAILURE', ph);
                                                        } else {
                                                            res.send({result: doc});
                                                        }
                                                    }
                                                );

                                                page.close();
                                                ph.exit();
                                                clearInterval(interval);
                                            }
                                        });
                                    }, 250);
                                }).catch(function () {
                                    terminateWithError('PAGE_CONNECT_FAILURE', ph);
                                });
                            }).catch(function () {
                                terminateWithError('VIEWPORT_RESIZE_FAILURE', ph);
                            });
                        }
                    }).catch(function () {
                        terminateWithError('PAGE_DEFINE_HEIGHT_FAILURE', ph);
                    });
                }).catch(function () {
                    terminateWithError('PAGE_CONNECT_FAILURE', ph);
                });
            }).catch(function () {
                terminateWithError('VIEWPORT_RESIZE_FAILURE', ph);
            });
        }).catch(function() {
            terminateWithError('PAGE_OPEN_FAILURE', ph);
        });
    }).catch(function() {
        terminateWithError('PHANTOM_INITIALIZATION_FAILURE');
    });
};

exports.uploadRemoteFile = function (req, res) {
    FileUploadStatus.create({}, function(err, fileUploadStatus) {
        var timeoutId,
            timeoutPeriod = 20000,
            remoteFileRequest = request({
                method: 'GET',
                timeout: timeoutPeriod,
                uri: req.query.url
            }, function () {})
                .on('response', function(response) {
                    clearTimeout(timeoutId);

                    if (response.statusCode === 200) {
                        var fileTotalSize = response.headers['content-length'] ? parseInt(response.headers['content-length'], 10) : -1;
                        var receivedDataSized = 0;
                        var filePath = '/tmp/' +
                            getFileName(new Date().getTime(), req.user._id, getFileExtension(req.query.url));

                        var file = fs.createWriteStream(filePath);
                        response.pipe(file);

                        response
                            .on('data', function(data) {
                                receivedDataSized += data.length;
                                fileUploadStatus.setCompletion(fileTotalSize > 0 ? receivedDataSized / fileTotalSize : 0);
                            })
                            .on('end', function() {
                                file.close(function() {
                                    fileUploadStatus.setCompletion(1);
                                    uploadFile({path: filePath, type: mime.lookup(filePath)}, req, function (err, doc) {
                                        if (!err) {
                                            fileUploadStatus.setDocument(doc);
                                        }
                                    });
                                });
                            })
                            .on('error', function(err) {
                                file.close(function() {
                                    fs.unlink(filePath);
                                    res.send({reason: err.toString()});
                                });
                            });

                        res.send({
                            _id: fileUploadStatus._id,
                            completion: fileUploadStatus.completion,
                            size: fileTotalSize
                        });

                    } else {
                        res.send({reason: 'The file is not found'});
                    }
                })
                .on('error', function(err) {
                    clearTimeout(timeoutId);
                    res.send({reason: err.toString()});
                });

            timeoutId = setTimeout(function() {
                remoteFileRequest.abort();
                res.send({reason: 'File request timeout'});
            }, timeoutPeriod);
    });
};

exports.fileCheck = function (req, res) {
    uploadFile(req.files.file, req, function (err, document) {
        if (err) {
            res.status(400);
            res.send({reason: err.toString()});
        } else {
            res.send(document);
        }
    });
};

exports.getDocuments = function (req, res) {
    var query = Doc.find(req.query);
    query.exec(function (err, collection) {
        res.send(collection);
    });
};

exports.getDocumentsByID = function (req, res) {
    Doc.findOne({_id: req.params.id}).exec(function (err, document) {
        res.send(document);
    });
};

exports.getUploadStatusDocument = function (req, res) {
    FileUploadStatus.findOne({_id: req.params.statusId}).exec(function (errorUploadStatus, uploadStatus) {
        if (errorUploadStatus) {
            res.status(500).send({reason: errorUploadStatus.toString()});
        } else if (!uploadStatus || (uploadStatus.document === undefined)) {
            res.status(404).send({reason: 'not found'});
        } else {
            Doc.findOne({_id: uploadStatus.document}).exec(function (errorDocument, document) {
                if (errorDocument) {
                    res.status(500).send({reason: errorDocument.toString()});
                } else if (!document) {
                    res.status(404).send({reason: 'not found'});
                } else {
                    res.send(document);
                }
            });
        }
    });
};

// exports.getUsersByID = function (req, res) {
//     User.findOne({_id: req.params.id}).exec(function (err, user) {
//         res.send(user);
//     });
// };

exports.updateDocument = function (req, res) {
    var document_update, timestamp;

    document_update = req.body;
    timestamp = new Date().toISOString();

    Doc.findOne({_id: document_update._id}).exec(function (err, document) {
        document.title = document_update.title;
        document.authors = document_update.authors;
        document.type = document_update.type;
        document.assessments = document_update.assessments;
        document.questions = document_update.questions;
        document.answers = document_update.answers;
        document.users = document_update.users;
        document.modified = document_update.modified;
        document.status = document_update.status;
        document.last_modified = {
            modified_by: req.user._id,
            modified_date: timestamp};

        var input_array = ['source', 'year', 'date_published', 'pages', 'volume', 'issue', 'websites', 'publisher', 'city', 'edition', 'institution', 'series', 'chapter', 'editors', 'country', 'translators', 'series_editor'];
        input_array.forEach(function (el) {
            if (el in document_update) {
                document[el] = document_update[el];
            }
        });

        document.save(function (err) {
            if (err) {
                res.status(400);
                res.send({reason: err.toString()});
            }
        });
    });
    // res.send();
};

exports.deleteDocument = function (req, res) {
    Doc.remove({_id: req.params.id}, function (err) {
        res.send(err ? {reason: err.toString()} : {});
    });
};
<commit_msg>prevent errors on empty pages<commit_after>'use strict';
/* global require */
var crypto              =   require('crypto'),
    fs                  =   require('fs'),
    mime                =   require('mime'),
    request             =   require('request'),
    s3                  =   require('s3'),
    Answer              =   require('mongoose').model('Answer'),
    Doc                 =   require('mongoose').model('Documents'),
    FileUploadStatus    =   require('mongoose').model('FileUploadStatus'),
    phantom             =   require('phantom'),
    //MendeleyToken     =   require('mongoose').model('MendeleyToken'),
    upload_bucket       =   process.env.DOC_BUCKET,
    client              =   s3.createClient({
                                maxAsyncS3: 20,     // this is the default
                                s3RetryCount: 3,    // this is the default
                                s3RetryDelay: 1000, // this is the default
                                multipartUploadThreshold: 20971520, // this is the default (20 MB)
                                multipartUploadSize: 15728640, // this is the default (15 MB)
                                s3Options: {
                                    accessKeyId: process.env.DOC_AWS_ACCESS_KEY,
                                    secretAccessKey: process.env.DOC_AWS_SECRET_KEY
                                    // any other options are passed to new AWS.S3()
                                    // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property
                                }
                            });

var uploadFile = function(file, req, callback) {
    var hash = crypto.createHash('sha1'),
        timestamp = new Date().toISOString(),
        file_extension = getFileExtension(file.path);

    fs.readFile(file.path, {encoding: 'utf8'}, function (err, data) {
        hash.update(data);
        // get hashed value of file as fingerprint
        var file_hash = hash.digest('hex');
        // search documents for hashed file
        Doc.findOne({file_hash: file_hash}, function (err, document) {
            // if file exists tag for reference
            if (document !== null) {
                callback(null, document);
                // if not upload to s3 with hashed value as file name,
                // create record with hash value and end url
            } else {
                // upload parameters
                var params = {
                    localFile: file.path,
                    s3Params: {
                        Bucket: upload_bucket,
                        Key: String(file_hash) + '.' + file_extension
                        // other options supported by putObject, except Body and ContentLength.
                        // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
                    }
                };

                var uploader = client.uploadFile(params);
                uploader.on('error', function(err) {
                    console.error("unable to upload:", err.stack);
                });
                uploader.on('progress', function() {
                    console.log("progress", uploader.progressMd5Amount, uploader.progressAmount, uploader.progressTotal);
                });
                uploader.on('end', function() {
                    console.log("done uploading");
                });

                Doc.create({
                    file_hash: file_hash,
                    mime_type: file.type,
                    s3_url: 'https://s3.amazonaws.com/' + upload_bucket + '/' + file_hash + '.' + file_extension,
                    modified: [{
                        modified_by: req.user._id,
                        modified_date: timestamp
                    }],
                    createdBy: req.user._id,
                    creationDate: timestamp
                }, callback);
            }
        });
    });
};

var getFileExtension = function(filePath) {
    return filePath.split('.')[filePath.split('.').length - 1];
};

var getFileName = function(timestamp, user, extension) {
    return user + '-' + timestamp + (extension ? ('.' + extension) : '');
};

exports.getRemoteFileUploadStatus = function (req, res) {
    FileUploadStatus.findOne({_id: req.params.statusId}, function(err, uploadStatus) {
        res.send(uploadStatus);
    });
};

exports.uploadUrlSnapshot = function(req, res) {
    var width = 1024, height = 768,
        terminateWithError = function(errorCode, ph) {
            res.send({error: errorCode});
            console.log(errorCode);

            if(ph) {
                ph.exit();
            }
        };

    phantom.create().then(function(ph) {
        ph.createPage().then(function(page) {
            page.property('viewportSize', {width: width, height: height}).then(function () {
                var timeout = setTimeout(function() {
                    terminateWithError('PAGE_LOADING_TIMEOUT_EXPIRED', ph);
                }, 30000);

                page.open(req.query.url).then(function () {
                    clearTimeout(timeout);

                    page.evaluate(function () {
                        if([undefined, null].indexOf(document) !== -1) {
                            return undefined;
                        }

                        if([undefined, null].indexOf(document.body) !== -1) {
                            return undefined;
                        }

                        if([undefined, null].indexOf(document.body.offsetHeight) !== -1) {
                            return undefined;
                        }

                        return document.body.offsetHeight;
                    }).then(function (actualHeight) {
                        if(actualHeight === undefined) {
                            terminateWithError('PAGE_DEFINE_HEIGHT_FAILURE', ph);
                        } else if (actualHeight > 3000) {
                            terminateWithError('TOO_LARGE_SIZE', ph);
                        } else {
                            page.property('viewportSize', {width: width, height: actualHeight}).then(function () {
                                page.open(req.query.url).then(function () {
                                    var filePath = '/tmp/' + getFileName(new Date().getTime(), req.user._id, 'png');
                                    page.render(filePath);

                                    var interval = setInterval(function() {
                                        fs.exists(filePath, function(renderingCompleted) {
                                            if (renderingCompleted) {
                                                uploadFile({path: filePath, type: mime.lookup(filePath)}, req,
                                                    function (errorUpload, doc) {
                                                        fs.unlink(filePath);

                                                        if (errorUpload) {
                                                            terminateWithError('S3_TRANSFER_FAILURE', ph);
                                                        } else {
                                                            res.send({result: doc});
                                                        }
                                                    }
                                                );

                                                page.close();
                                                ph.exit();
                                                clearInterval(interval);
                                            }
                                        });
                                    }, 250);
                                }).catch(function () {
                                    terminateWithError('PAGE_CONNECT_FAILURE', ph);
                                });
                            }).catch(function () {
                                terminateWithError('VIEWPORT_RESIZE_FAILURE', ph);
                            });
                        }
                    }).catch(function () {
                        terminateWithError('PAGE_DEFINE_HEIGHT_FAILURE', ph);
                    });
                }).catch(function () {
                    terminateWithError('PAGE_CONNECT_FAILURE', ph);
                });
            }).catch(function () {
                terminateWithError('VIEWPORT_RESIZE_FAILURE', ph);
            });
        }).catch(function() {
            terminateWithError('PAGE_OPEN_FAILURE', ph);
        });
    }).catch(function() {
        terminateWithError('PHANTOM_INITIALIZATION_FAILURE');
    });
};

exports.uploadRemoteFile = function (req, res) {
    FileUploadStatus.create({}, function(err, fileUploadStatus) {
        var timeoutId,
            timeoutPeriod = 20000,
            remoteFileRequest = request({
                method: 'GET',
                timeout: timeoutPeriod,
                uri: req.query.url
            }, function () {})
                .on('response', function(response) {
                    clearTimeout(timeoutId);

                    if (response.statusCode === 200) {
                        var fileTotalSize = response.headers['content-length'] ? parseInt(response.headers['content-length'], 10) : -1;
                        var receivedDataSized = 0;
                        var filePath = '/tmp/' +
                            getFileName(new Date().getTime(), req.user._id, getFileExtension(req.query.url));

                        var file = fs.createWriteStream(filePath);
                        response.pipe(file);

                        response
                            .on('data', function(data) {
                                receivedDataSized += data.length;
                                fileUploadStatus.setCompletion(fileTotalSize > 0 ? receivedDataSized / fileTotalSize : 0);
                            })
                            .on('end', function() {
                                file.close(function() {
                                    fileUploadStatus.setCompletion(1);
                                    uploadFile({path: filePath, type: mime.lookup(filePath)}, req, function (err, doc) {
                                        if (!err) {
                                            fileUploadStatus.setDocument(doc);
                                        }
                                    });
                                });
                            })
                            .on('error', function(err) {
                                file.close(function() {
                                    fs.unlink(filePath);
                                    res.send({reason: err.toString()});
                                });
                            });

                        res.send({
                            _id: fileUploadStatus._id,
                            completion: fileUploadStatus.completion,
                            size: fileTotalSize
                        });

                    } else {
                        res.send({reason: 'The file is not found'});
                    }
                })
                .on('error', function(err) {
                    clearTimeout(timeoutId);
                    res.send({reason: err.toString()});
                });

            timeoutId = setTimeout(function() {
                remoteFileRequest.abort();
                res.send({reason: 'File request timeout'});
            }, timeoutPeriod);
    });
};

exports.fileCheck = function (req, res) {
    uploadFile(req.files.file, req, function (err, document) {
        if (err) {
            res.status(400);
            res.send({reason: err.toString()});
        } else {
            res.send(document);
        }
    });
};

exports.getDocuments = function (req, res) {
    var query = Doc.find(req.query);
    query.exec(function (err, collection) {
        res.send(collection);
    });
};

exports.getDocumentsByID = function (req, res) {
    Doc.findOne({_id: req.params.id}).exec(function (err, document) {
        res.send(document);
    });
};

exports.getUploadStatusDocument = function (req, res) {
    FileUploadStatus.findOne({_id: req.params.statusId}).exec(function (errorUploadStatus, uploadStatus) {
        if (errorUploadStatus) {
            res.status(500).send({reason: errorUploadStatus.toString()});
        } else if (!uploadStatus || (uploadStatus.document === undefined)) {
            res.status(404).send({reason: 'not found'});
        } else {
            Doc.findOne({_id: uploadStatus.document}).exec(function (errorDocument, document) {
                if (errorDocument) {
                    res.status(500).send({reason: errorDocument.toString()});
                } else if (!document) {
                    res.status(404).send({reason: 'not found'});
                } else {
                    res.send(document);
                }
            });
        }
    });
};

// exports.getUsersByID = function (req, res) {
//     User.findOne({_id: req.params.id}).exec(function (err, user) {
//         res.send(user);
//     });
// };

exports.updateDocument = function (req, res) {
    var document_update, timestamp;

    document_update = req.body;
    timestamp = new Date().toISOString();

    Doc.findOne({_id: document_update._id}).exec(function (err, document) {
        document.title = document_update.title;
        document.authors = document_update.authors;
        document.type = document_update.type;
        document.assessments = document_update.assessments;
        document.questions = document_update.questions;
        document.answers = document_update.answers;
        document.users = document_update.users;
        document.modified = document_update.modified;
        document.status = document_update.status;
        document.last_modified = {
            modified_by: req.user._id,
            modified_date: timestamp};

        var input_array = ['source', 'year', 'date_published', 'pages', 'volume', 'issue', 'websites', 'publisher', 'city', 'edition', 'institution', 'series', 'chapter', 'editors', 'country', 'translators', 'series_editor'];
        input_array.forEach(function (el) {
            if (el in document_update) {
                document[el] = document_update[el];
            }
        });

        document.save(function (err) {
            if (err) {
                res.status(400);
                res.send({reason: err.toString()});
            }
        });
    });
    // res.send();
};

exports.deleteDocument = function (req, res) {
    Doc.remove({_id: req.params.id}, function (err) {
        res.send(err ? {reason: err.toString()} : {});
    });
};
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useCallback, useRef } from 'react';
import { __ } from '@web-stories-wp/i18n';
import styled from 'styled-components';

/**
 * Internal dependencies
 */
import { useStory } from '../../../../app/story';
import { Row, TextArea } from '../../../form';
import { SimplePanel } from '../../panel';
import { useFocusHighlight, states, styles } from '../../../../app/highlights';
import { Text, THEME_CONSTANTS } from '../../../../../design-system';

// Margin -4px is making up for extra margin added by rows.
const StyledText = styled(Text)`
  color: ${({ theme }) => theme.colors.fg.tertiary};
  margin-top: -4px;
`;

export const EXCERPT_MAX_LENGTH = 100;

function ExcerptPanel() {
  const { excerpt, updateStory } = useStory(
    ({
      state: {
        story: { excerpt = '' },
      },
      actions: { updateStory },
    }) => ({ excerpt, updateStory })
  );

  const handleTextChange = useCallback(
    (evt) => {
      updateStory({
        properties: { excerpt: evt.target.value },
      });
    },
    [updateStory]
  );

  const ref = useRef();
  const highlight = useFocusHighlight(states.EXCERPT, ref);

  return (
    <SimplePanel
      css={highlight?.showEffect && styles.FLASH}
      name="excerpt"
      title={__('Story Description', 'web-stories')}
      collapsedByDefault={false}
      isPersistable={!highlight}
    >
      <Row>
        <TextArea
          ref={ref}
          value={excerpt}
          onChange={handleTextChange}
          placeholder={__('Write a description of the story', 'web-stories')}
          aria-label={__('Story Description', 'web-stories')}
          maxLength={EXCERPT_MAX_LENGTH}
          showCount
          rows={4}
          css={highlight?.showEffect && styles.OUTLINE}
        />
      </Row>
      <Row>
        <StyledText size={THEME_CONSTANTS.TYPOGRAPHY.PRESET_SIZES.X_SMALL}>
          {__(
            'Stories with a description tend to do better on search and have a wider reach',
            'web-stories'
          )}
        </StyledText>
      </Row>
    </SimplePanel>
  );
}

export default ExcerptPanel;
<commit_msg>Revert max size. (#7326)<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useCallback, useRef } from 'react';
import { __ } from '@web-stories-wp/i18n';
import styled from 'styled-components';

/**
 * Internal dependencies
 */
import { useStory } from '../../../../app/story';
import { Row, TextArea } from '../../../form';
import { SimplePanel } from '../../panel';
import { useFocusHighlight, states, styles } from '../../../../app/highlights';
import { Text, THEME_CONSTANTS } from '../../../../../design-system';

// Margin -4px is making up for extra margin added by rows.
const StyledText = styled(Text)`
  color: ${({ theme }) => theme.colors.fg.tertiary};
  margin-top: -4px;
`;

export const EXCERPT_MAX_LENGTH = 200;

function ExcerptPanel() {
  const { excerpt, updateStory } = useStory(
    ({
      state: {
        story: { excerpt = '' },
      },
      actions: { updateStory },
    }) => ({ excerpt, updateStory })
  );

  const handleTextChange = useCallback(
    (evt) => {
      updateStory({
        properties: { excerpt: evt.target.value },
      });
    },
    [updateStory]
  );

  const ref = useRef();
  const highlight = useFocusHighlight(states.EXCERPT, ref);

  return (
    <SimplePanel
      css={highlight?.showEffect && styles.FLASH}
      name="excerpt"
      title={__('Story Description', 'web-stories')}
      collapsedByDefault={false}
      isPersistable={!highlight}
    >
      <Row>
        <TextArea
          ref={ref}
          value={excerpt}
          onChange={handleTextChange}
          placeholder={__('Write a description of the story', 'web-stories')}
          aria-label={__('Story Description', 'web-stories')}
          maxLength={EXCERPT_MAX_LENGTH}
          showCount
          rows={4}
          css={highlight?.showEffect && styles.OUTLINE}
        />
      </Row>
      <Row>
        <StyledText size={THEME_CONSTANTS.TYPOGRAPHY.PRESET_SIZES.X_SMALL}>
          {__(
            'Stories with a description tend to do better on search and have a wider reach',
            'web-stories'
          )}
        </StyledText>
      </Row>
    </SimplePanel>
  );
}

export default ExcerptPanel;
<|endoftext|>


<commit_before>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
//  Phaser.Sound.WebAudioSound
// TODO support webkitAudioContext implementation differences
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
var WebAudioSound = new Class({
    Extends: BaseSound,
    initialize: function WebAudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * [description]
         *
         * @private
         * @property {AudioBuffer} audioBuffer
         */
        this.audioBuffer = manager.game.cache.audio.get(key);
        if (!this.audioBuffer) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * [description]
         *
         * @private
         * @property {AudioBufferSourceNode} source
         */
        this.source = null;
        /**
         * [description]
         *
         * @private
         * @property {GainNode} muteNode
         */
        this.muteNode = manager.context.createGain();
        /**
         * [description]
         *
         * @private
         * @property {GainNode} volumeNode
         */
        this.volumeNode = manager.context.createGain();
        /**
         * The time the previous playback started at based on
         * BaseAudioContext.currentTime value.
         *
         * @private
         * @property {number} startTime
         */
        this.startTime = 0;
        /**
         * An array where we keep track of all rate updates during playback.
         *
         * @private
         * @property {{ time: number, rate: number }[]} rateUpdates
         */
        this.rateUpdates = [];
        /**
         * Used for keeping track when sound source playback has ended
         * so it's state can be updated accordingly.
         *
         * @private
         * @property {boolean} hasEnded
         */
        this.hasEnded = false;
        this.muteNode.connect(this.volumeNode);
        this.volumeNode.connect(manager.destination);
        this.duration = this.audioBuffer.duration;
        this.totalDuration = this.audioBuffer.duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return null;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        this.stopAndRemoveBufferSource();
        this.createAndStartBufferSource();
        return this;
    },
    pause: function () {
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.getCurrentTime(); // Equivalent to setting paused time
        this.stopAndRemoveBufferSource();
        return true;
    },
    resume: function () {
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        this.createAndStartBufferSource();
        return true;
    },
    stop: function () {
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndRemoveBufferSource();
        return true;
    },
    /**
     * Used internally to do what the name says.
     *
     * @private
     */
    // TODO add delay param
    createAndStartBufferSource: function () {
        var seek = this.currentConfig.seek;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        var duration = this.duration - seek;
        this.startTime = this.manager.context.currentTime - seek;
        this.source = this.manager.context.createBufferSource();
        this.source.buffer = this.audioBuffer;
        this.source.connect(this.muteNode);
        this.source.onended = function (ev) {
            if (ev.target === this.source) {
                // sound ended
                this.hasEnded = true;
            }
            // else was stopped
        }.bind(this);
        this.applyConfig();
        this.source.start(0, Math.max(0, offset), Math.max(0, duration));
        this.resetConfig();
    },
    /**
     * Used internally to do what the name says.
     *
     * @private
     */
    stopAndRemoveBufferSource: function () {
        if (this.source) {
            this.source.stop();
            this.source = null;
        }
        this.startTime = 0;
    },
    /**
     * @protected
     */
    applyConfig: function () {
        this.rateUpdates.length = 0;
        this.rateUpdates.push({
            time: 0,
            rate: 1
        });
        BaseSound.prototype.applyConfig.call(this);
    },
    /**
     * Update method called on every game step.
     *
     * @param {number} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
     * @param {number} delta - The delta time elapsed since the last frame.
     */
    update: function (time, delta) {
        if (this.hasEnded) {
            this.hasEnded = false;
            this.stop();
        }
    },
    destroy: function () {
    },
    /**
     * @private
     */
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.source) {
            this.source.playbackRate.setValueAtTime(this.totalRate, 0);
        }
        if (this.isPlaying) {
            this.rateUpdates.push({
                time: this.manager.context.currentTime - this.startTime,
                rate: this.totalRate
            });
        }
    },
    /**
     * @private
     */
    getCurrentTime: function () {
        var currentTime = 0;
        for (var i = 0; i < this.rateUpdates.length; i++) {
            var nextTime = void 0;
            if (i < this.rateUpdates.length - 1) {
                nextTime = this.rateUpdates[i + 1].time;
            }
            else {
                nextTime = this.manager.context.currentTime - this.startTime;
            }
            currentTime += (nextTime - this.rateUpdates[i].time) * this.rateUpdates[i].rate;
        }
        return currentTime;
    }
});
/**
 * Mute setting.
 * @property {boolean} mute
 */
Object.defineProperty(WebAudioSound.prototype, 'mute', {
    get: function () {
        return this.muteNode.gain.value === 0;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.muteNode.gain.setValueAtTime(value ? 0 : 1, 0);
    }
});
/**
 * Volume setting.
 * @property {number} volume
 */
Object.defineProperty(WebAudioSound.prototype, 'volume', {
    get: function () {
        return this.volumeNode.gain.value;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.volumeNode.gain.setValueAtTime(value, 0);
    }
});
/**
 * Playback rate.
 * @property {number} rate
 */
Object.defineProperty(WebAudioSound.prototype, 'rate', {
    get: function () {
        return this.currentConfig.rate;
    },
    set: function (value) {
        this.currentConfig.rate = value;
        this.setRate();
    }
});
/**
 * Detuning of sound.
 * @property {number} detune
 */
Object.defineProperty(WebAudioSound.prototype, 'detune', {
    get: function () {
        return this.currentConfig.detune;
    },
    set: function (value) {
        this.currentConfig.detune = value;
        this.setRate();
    }
});
/**
 * Current position of playing sound.
 * @property {number} seek
 */
Object.defineProperty(WebAudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.getCurrentTime();
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            this.currentConfig.seek = value;
        }
        if (this.isPlaying) {
            this.stopAndRemoveBufferSource();
            this.createAndStartBufferSource();
        }
    }
});
module.exports = WebAudioSound;
<commit_msg>Implemented destroy method for WebAudioSound class<commit_after>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
//  Phaser.Sound.WebAudioSound
// TODO support webkitAudioContext implementation differences
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
var WebAudioSound = new Class({
    Extends: BaseSound,
    initialize: function WebAudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * [description]
         *
         * @private
         * @property {AudioBuffer} audioBuffer
         */
        this.audioBuffer = manager.game.cache.audio.get(key);
        if (!this.audioBuffer) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * [description]
         *
         * @private
         * @property {AudioBufferSourceNode} source
         */
        this.source = null;
        /**
         * [description]
         *
         * @private
         * @property {GainNode} muteNode
         */
        this.muteNode = manager.context.createGain();
        /**
         * [description]
         *
         * @private
         * @property {GainNode} volumeNode
         */
        this.volumeNode = manager.context.createGain();
        /**
         * The time the previous playback started at based on
         * BaseAudioContext.currentTime value.
         *
         * @private
         * @property {number} startTime
         */
        this.startTime = 0;
        /**
         * An array where we keep track of all rate updates during playback.
         *
         * @private
         * @property {{ time: number, rate: number }[]} rateUpdates
         */
        this.rateUpdates = [];
        /**
         * Used for keeping track when sound source playback has ended
         * so it's state can be updated accordingly.
         *
         * @private
         * @property {boolean} hasEnded
         */
        this.hasEnded = false;
        this.muteNode.connect(this.volumeNode);
        this.volumeNode.connect(manager.destination);
        this.duration = this.audioBuffer.duration;
        this.totalDuration = this.audioBuffer.duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return null;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        this.stopAndRemoveBufferSource();
        this.createAndStartBufferSource();
        return this;
    },
    pause: function () {
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.getCurrentTime(); // Equivalent to setting paused time
        this.stopAndRemoveBufferSource();
        return true;
    },
    resume: function () {
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        this.createAndStartBufferSource();
        return true;
    },
    stop: function () {
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndRemoveBufferSource();
        return true;
    },
    /**
     * Used internally to do what the name says.
     *
     * @private
     */
    // TODO add delay param
    createAndStartBufferSource: function () {
        var seek = this.currentConfig.seek;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        var duration = this.duration - seek;
        this.startTime = this.manager.context.currentTime - seek;
        this.source = this.manager.context.createBufferSource();
        this.source.buffer = this.audioBuffer;
        this.source.connect(this.muteNode);
        this.source.onended = function (ev) {
            if (ev.target === this.source) {
                // sound ended
                this.hasEnded = true;
            }
            // else was stopped
        }.bind(this);
        this.applyConfig();
        this.source.start(0, Math.max(0, offset), Math.max(0, duration));
        this.resetConfig();
    },
    /**
     * Used internally to do what the name says.
     *
     * @private
     */
    stopAndRemoveBufferSource: function () {
        if (this.source) {
            this.source.stop();
            this.source = null;
        }
        this.startTime = 0;
    },
    /**
     * @protected
     */
    applyConfig: function () {
        this.rateUpdates.length = 0;
        this.rateUpdates.push({
            time: 0,
            rate: 1
        });
        BaseSound.prototype.applyConfig.call(this);
    },
    /**
     * Update method called on every game step.
     *
     * @param {number} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
     * @param {number} delta - The delta time elapsed since the last frame.
     */
    update: function (time, delta) {
        if (this.hasEnded) {
            this.hasEnded = false;
            this.stop();
        }
    },
    destroy: function () {
        BaseSound.prototype.destroy.call(this);
        this.audioBuffer = null;
        this.stopAndRemoveBufferSource();
        this.muteNode.disconnect();
        this.muteNode = null;
        this.volumeNode.disconnect();
        this.volumeNode = null;
        this.rateUpdates = null;
    },
    /**
     * @private
     */
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.source) {
            this.source.playbackRate.setValueAtTime(this.totalRate, 0);
        }
        if (this.isPlaying) {
            this.rateUpdates.push({
                time: this.manager.context.currentTime - this.startTime,
                rate: this.totalRate
            });
        }
    },
    /**
     * @private
     */
    getCurrentTime: function () {
        var currentTime = 0;
        for (var i = 0; i < this.rateUpdates.length; i++) {
            var nextTime = void 0;
            if (i < this.rateUpdates.length - 1) {
                nextTime = this.rateUpdates[i + 1].time;
            }
            else {
                nextTime = this.manager.context.currentTime - this.startTime;
            }
            currentTime += (nextTime - this.rateUpdates[i].time) * this.rateUpdates[i].rate;
        }
        return currentTime;
    }
});
/**
 * Mute setting.
 * @property {boolean} mute
 */
Object.defineProperty(WebAudioSound.prototype, 'mute', {
    get: function () {
        return this.muteNode.gain.value === 0;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.muteNode.gain.setValueAtTime(value ? 0 : 1, 0);
    }
});
/**
 * Volume setting.
 * @property {number} volume
 */
Object.defineProperty(WebAudioSound.prototype, 'volume', {
    get: function () {
        return this.volumeNode.gain.value;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.volumeNode.gain.setValueAtTime(value, 0);
    }
});
/**
 * Playback rate.
 * @property {number} rate
 */
Object.defineProperty(WebAudioSound.prototype, 'rate', {
    get: function () {
        return this.currentConfig.rate;
    },
    set: function (value) {
        this.currentConfig.rate = value;
        this.setRate();
    }
});
/**
 * Detuning of sound.
 * @property {number} detune
 */
Object.defineProperty(WebAudioSound.prototype, 'detune', {
    get: function () {
        return this.currentConfig.detune;
    },
    set: function (value) {
        this.currentConfig.detune = value;
        this.setRate();
    }
});
/**
 * Current position of playing sound.
 * @property {number} seek
 */
Object.defineProperty(WebAudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.getCurrentTime();
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            this.currentConfig.seek = value;
        }
        if (this.isPlaying) {
            this.stopAndRemoveBufferSource();
            this.createAndStartBufferSource();
        }
    }
});
module.exports = WebAudioSound;
<|endoftext|>


<commit_before>import UAParser from 'ua-parser-js';

export const getDeviceClass = () => {
    // Detect device model from UA
    const parser = new UAParser();
    const uastring = navigator.userAgent;
    parser.setUA(uastring);
    const device = parser.getDevice();
    const model = device.model;
    console.log('[helpers] device mode ==>', model);

    // Match against devices you consider low-end
    const lowEnd = [
        'Nexus 4',
        'Nexus 5',
        'Nexus 5X',
        'Nexus 6',
        'Redmi Note 6 Pro',
        'ONE' // Alcatel 1X
    ];
    // Optional: map to device-year-class, Geekbench.
    return lowEnd.indexOf(model) > 0 ? 'light' : 'heavy'
};
<commit_msg>fixed console log convention(minor) in cra-ua-aware-code-splitting<commit_after>/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import UAParser from 'ua-parser-js';

export const getDeviceClass = () => {
    // Detect device model from UA
    const parser = new UAParser();
    const uastring = navigator.userAgent;
    parser.setUA(uastring);
    const device = parser.getDevice();
    const model = device.model;
    console.log('[helpers] device mode => ', model);

    // Match against devices you consider low-end
    const lowEnd = [
        'Nexus 4',
        'Nexus 5',
        'Nexus 5X',
        'Nexus 6',
        'Redmi Note 6 Pro',
        'ONE' // Alcatel 1X
    ];
    // Optional: map to device-year-class, Geekbench.
    return lowEnd.indexOf(model) > 0 ? 'light' : 'heavy'
};
<|endoftext|>