<commit_before>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

var requirejs = require("requirejs"),
    express = require('express'),
    passport = require('passport'),
    stratGugli = require('passport-google').Strategy,
    path = require('path'),
    https = require('https'),
    http = require('http');

requirejs.config({
    nodeRequire: require,
    baseUrl: __dirname + "/..",
    paths: {
        "core":"core",
        "logManager": "common/LogManager",
        "util": "util",
        "storage": "storage",
        "user": "user",
        "config": 'config',
        "cli": 'cli',
        "bin": 'bin',
        "auth": 'auth',
        "coreclient": 'coreclient'

    }
});

requirejs(['logManager',
    'bin/getconfig',
    'storage/serverstorage',
    'storage/server',
    'storage/cache',
    'storage/mongo',
    'storage/log',
    'auth/sessionstore',
    'auth/vehicleforgeauth',
    'auth/gmeauth'],function(
    logManager,
    CONFIG,
    Storage,
    Server,
    Cache,
    Mongo,
    Log,
    SStore,
    VFAUTH,
    GMEAUTH){
    var parameters = CONFIG;
    var logLevel = parameters.loglevel || logManager.logLevels.WARNING;
    var logFile = parameters.logfile || 'server.log';
    logManager.setLogLevel(logLevel);
    logManager.useColors(true);
    logManager.setFileLogPath(logFile);
    var logger = logManager.create("combined-server");
    var iologger = logManager.create("socket.io");
    var sitekey = null;
    var sitecertificate = null;
    if(parameters.httpsecure){
        sitekey = require('fs').readFileSync("proba-key.pem");
        sitecertificate = require('fs').readFileSync("proba-cert.pem");
    }
    var app = express();

    var __sessionStore = new SStore();

    var forge = new VFAUTH({session:__sessionStore});
    var gme = new GMEAUTH({session:__sessionStore,host:parameters.mongoip,port:parameters.mongoport,database:parameters.mongodatabase,guest:parameters.guest});

    var globalAuthorization = function(sessionId,projectName,type,callback){
        __sessionStore.get(sessionId,function(err,data){
            if(!err && data){
                switch (data.userType){
                    case 'GME':
                        gme.authorize(sessionId,projectName,type,callback);
                        break;
                    case 'vehicleForge':
                        forge.authorize(sessionId,projectName,type,callback);
                        break;
                    default:
                        callback('unknown user type',false);
                }
            } else {
                err = err || 'session not found';
                callback(err,false);
            }
        });
    };

    //for session handling we save the user data to the memory and reuse them in case of need
    var _users = {};
    passport.serializeUser(function(user, done) {
        _users[user.id] = user;
        done(null, user.id);
    });
    passport.deserializeUser(function(id, done) {
        done(null,_users[id]);
    });

    passport.use(new stratGugli({
            returnURL: parameters.host+':'+parameters.port+'/login/google/return',
            realm: parameters.host+':'+parameters.port
        },
        function(identifier, profile, done) {
            return done(null,{id:profile.emails[0].value});
        }
    ));

    function ensureAuthenticated(req, res, next) {
        if(true === parameters.authentication){
            if(req.isAuthenticated() || (req.session && true === req.session.authenticated)){
                return next();
            }
            res.redirect('/login');
        } else {
            return next();
        }
    }
    function checkVF(req,res,next){
        console.log('check Vehicle Forge framework');
        if(req.isAuthenticated() || (req.session && true === req.session.authenticated)){
            return next();
        } else {
            if(req.cookies['isisforge']){
                res.redirect('/login/forge');
            } else {
                return next();
            }
        }
    }

    var staticclientdirpath = path.resolve(__dirname+'./../client');
    var staticdirpath = path.resolve(__dirname+'./..');

    app.configure(function(){
        app.use(express.logger());
        app.use(express.cookieParser());
        app.use(express.bodyParser());
        app.use(express.methodOverride());
        app.use(express.session({store: __sessionStore, secret: parameters.sessioncookiesecret, key: parameters.sessioncookieid }));
        app.use(passport.initialize());
        app.use(passport.session());
        app.use(app.router);
    });
    //GET

    //starting point
    app.get('/',checkVF,ensureAuthenticated,function(req,res){
        res.sendfile(staticclientdirpath+'/index.html',{user:req.user},function(err){
            res.send(404);
        });
    });

    //authentication related routing
    app.get('/logout', function(req, res){
        res.clearCookie('webgme');
        res.clearCookie('isisforge'); //todo is this really needed
        req.logout();
        req.session.authenticated = false;
        req.session.userType = 'unknown';
        res.redirect('/');
    });
    app.get('/login',function(req,res){
        res.sendfile(staticclientdirpath+'/login.html',{},function(err){
            res.send(404);
        });
    });
    app.post('/login',gme.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });
    app.get('/login/google',passport.authenticate('google'));
    app.get('/login/google/return',gme.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });
    app.get('/login/forge',forge.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });

    //static contents
    //javascripts - core and transportation related files
    app.get(/^\/(common|util|storage|core|config|auth|bin|coreclient)\/.*\.js/,ensureAuthenticated,function(req,res){
        res.sendfile(path.join(staticdirpath,req.path),function(err){
            res.send(404);
        });
    });
    //client contents - js/html/css
    //css classified as not secure content
    app.get(/^\/.*\.(css|ico)/,function(req,res){
        res.sendfile(staticclientdirpath+req.path,function(err){
            res.send(404);
        });
    });
    app.get(/^\/.*\.(js|html|gif|png|bmp|svg)/,ensureAuthenticated,function(req,res){
        res.sendfile(staticclientdirpath+req.path,function(err){
            res.send(404);
        });
    });
    //rest functionality
    app.get('/rest/*',function(req,res){
        res.send(500);
    });
    //other get
    app.get('*',function(req,res){
        res.send(500);
    });

    var httpServer = null;
    if(parameters.httpsecure){
        httpServer = https.createServer({key:sitekey,cert:sitecertificate}, app).listen(parameters.port);
    } else {
        httpServer = http.createServer(app).listen(parameters.port);
    }

    var storage = null;
    var __storageOptions = {combined:httpServer,logger:iologger,session:false,cookieID:parameters.sessioncookieid};
    if(true === parameters.authentication){
        __storageOptions.session = true;
        __storageOptions.sessioncheck = __sessionStore.check;
        __storageOptions.secret = parameters.sessioncookiesecret;
        __storageOptions.authorization = globalAuthorization;
        __storageOptions.authInfo = gme.getAuthorizationInfo;
    }

    __storageOptions.host = parameters.mongoip;
    __storageOptions.port = parameters.mongoport;
    __storageOptions.database = parameters.mongodatabase;
    __storageOptions.log = logManager.create('combined-server-storage');

    storage = Storage(__storageOptions);

    storage.open();
});<commit_msg>cookie domain problem fix<commit_after>/*
 * Copyright (C) 2013 Vanderbilt University, All rights reserved.
 *
 * Author: Tamas Kecskes
 */

var requirejs = require("requirejs"),
    express = require('express'),
    passport = require('passport'),
    stratGugli = require('passport-google').Strategy,
    path = require('path'),
    https = require('https'),
    http = require('http');

requirejs.config({
    nodeRequire: require,
    baseUrl: __dirname + "/..",
    paths: {
        "core":"core",
        "logManager": "common/LogManager",
        "util": "util",
        "storage": "storage",
        "user": "user",
        "config": 'config',
        "cli": 'cli',
        "bin": 'bin',
        "auth": 'auth',
        "coreclient": 'coreclient'

    }
});

requirejs(['logManager',
    'bin/getconfig',
    'storage/serverstorage',
    'storage/server',
    'storage/cache',
    'storage/mongo',
    'storage/log',
    'auth/sessionstore',
    'auth/vehicleforgeauth',
    'auth/gmeauth'],function(
    logManager,
    CONFIG,
    Storage,
    Server,
    Cache,
    Mongo,
    Log,
    SStore,
    VFAUTH,
    GMEAUTH){
    var parameters = CONFIG;
    var logLevel = parameters.loglevel || logManager.logLevels.WARNING;
    var logFile = parameters.logfile || 'server.log';
    logManager.setLogLevel(logLevel);
    logManager.useColors(true);
    logManager.setFileLogPath(logFile);
    var logger = logManager.create("combined-server");
    var iologger = logManager.create("socket.io");
    var sitekey = null;
    var sitecertificate = null;
    if(parameters.httpsecure){
        sitekey = require('fs').readFileSync("proba-key.pem");
        sitecertificate = require('fs').readFileSync("proba-cert.pem");
    }
    var app = express();

    var __sessionStore = new SStore();

    var forge = new VFAUTH({session:__sessionStore});
    var gme = new GMEAUTH({session:__sessionStore,host:parameters.mongoip,port:parameters.mongoport,database:parameters.mongodatabase,guest:parameters.guest});

    var globalAuthorization = function(sessionId,projectName,type,callback){
        __sessionStore.get(sessionId,function(err,data){
            if(!err && data){
                switch (data.userType){
                    case 'GME':
                        gme.authorize(sessionId,projectName,type,callback);
                        break;
                    case 'vehicleForge':
                        forge.authorize(sessionId,projectName,type,callback);
                        break;
                    default:
                        callback('unknown user type',false);
                }
            } else {
                err = err || 'session not found';
                callback(err,false);
            }
        });
    };

    //for session handling we save the user data to the memory and reuse them in case of need
    var _users = {};
    passport.serializeUser(function(user, done) {
        _users[user.id] = user;
        done(null, user.id);
    });
    passport.deserializeUser(function(id, done) {
        done(null,_users[id]);
    });

    passport.use(new stratGugli({
            returnURL: parameters.host+':'+parameters.port+'/login/google/return',
            realm: parameters.host+':'+parameters.port
        },
        function(identifier, profile, done) {
            return done(null,{id:profile.emails[0].value});
        }
    ));

    function ensureAuthenticated(req, res, next) {
        if(true === parameters.authentication){
            if(req.isAuthenticated() || (req.session && true === req.session.authenticated)){
                return next();
            }
            res.redirect('/login');
        } else {
            return next();
        }
    }
    function checkVF(req,res,next){
        console.log('check Vehicle Forge framework');
        if(req.isAuthenticated() || (req.session && true === req.session.authenticated)){
            return next();
        } else {
            if(req.cookies['isisforge']){
                res.redirect('/login/forge');
            } else {
                return next();
            }
        }
    }

    var staticclientdirpath = path.resolve(__dirname+'./../client');
    var staticdirpath = path.resolve(__dirname+'./..');

    app.configure(function(){
        app.use(express.logger());
        app.use(express.cookieParser());
        app.use(express.bodyParser());
        app.use(express.methodOverride());
        app.use(express.session({store: __sessionStore, secret: parameters.sessioncookiesecret, key: parameters.sessioncookieid, cookie: { domain:parameters.host} }));
        app.use(passport.initialize());
        app.use(passport.session());
        app.use(app.router);
    });
    //GET

    //starting point
    app.get('/',checkVF,ensureAuthenticated,function(req,res){
        res.sendfile(staticclientdirpath+'/index.html',{user:req.user},function(err){
            res.send(404);
        });
    });

    //authentication related routing
    app.get('/logout', function(req, res){
        res.clearCookie('webgme');
        res.clearCookie('isisforge'); //todo is this really needed
        req.logout();
        req.session.authenticated = false;
        req.session.userType = 'unknown';
        res.redirect('/');
    });
    app.get('/login',function(req,res){
        res.sendfile(staticclientdirpath+'/login.html',{},function(err){
            res.send(404);
        });
    });
    app.post('/login',gme.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });
    app.get('/login/google',passport.authenticate('google'));
    app.get('/login/google/return',gme.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });
    app.get('/login/forge',forge.authenticate,function(req,res){
        res.cookie('webgme',req.session.udmId);
        res.redirect('/');
    });

    //static contents
    //javascripts - core and transportation related files
    app.get(/^\/(common|util|storage|core|config|auth|bin|coreclient)\/.*\.js/,ensureAuthenticated,function(req,res){
        res.sendfile(path.join(staticdirpath,req.path),function(err){
            res.send(404);
        });
    });
    //client contents - js/html/css
    //css classified as not secure content
    app.get(/^\/.*\.(css|ico)/,function(req,res){
        res.sendfile(staticclientdirpath+req.path,function(err){
            res.send(404);
        });
    });
    app.get(/^\/.*\.(js|html|gif|png|bmp|svg)/,ensureAuthenticated,function(req,res){
        res.sendfile(staticclientdirpath+req.path,function(err){
            res.send(404);
        });
    });
    //rest functionality
    app.get('/rest/*',function(req,res){
        res.send(500);
    });
    //other get
    app.get('*',function(req,res){
        res.send(500);
    });

    var httpServer = null;
    if(parameters.httpsecure){
        httpServer = https.createServer({key:sitekey,cert:sitecertificate}, app).listen(parameters.port);
    } else {
        httpServer = http.createServer(app).listen(parameters.port);
    }

    var storage = null;
    var __storageOptions = {combined:httpServer,logger:iologger,session:false,cookieID:parameters.sessioncookieid};
    if(true === parameters.authentication){
        __storageOptions.session = true;
        __storageOptions.sessioncheck = __sessionStore.check;
        __storageOptions.secret = parameters.sessioncookiesecret;
        __storageOptions.authorization = globalAuthorization;
        __storageOptions.authInfo = gme.getAuthorizationInfo;
    }

    __storageOptions.host = parameters.mongoip;
    __storageOptions.port = parameters.mongoport;
    __storageOptions.database = parameters.mongodatabase;
    __storageOptions.log = logManager.create('combined-server-storage');

    storage = Storage(__storageOptions);

    storage.open();
});<|endoftext|>


<commit_before>/* ======= Global Utilities ======= */

/**
 * Creates Default Metadata
 * @return {Object} Metadata
 */
var defaultMeta = function() {
  return {
    shouldRender: true
  }
}

/**
 * Creates a Virtual DOM Node
 * @param {String} type
 * @param {String} val
 * @param {Object} props
 * @param {Array} children
 * @param {Object} meta
 * @return {Object} Virtual DOM Node
 */
var createElement = function(type, val, props, children, meta) {
  return {
    type: type,
    val: val,
    props: props,
    children: children,
    meta: meta
  };
}

/**
 * Adds Nodes to Rendered Virtual DOM
 * @param {Object} rdom
 * @param {Object} vdom
 * @return {Object} Rendered Virtual DOM with Nodes
 */
var addNodes = function(rdom, vdom) {
  rdom.node = vdom.node;
  for(var vnode in rdom.children) {
    rdom.children[vnode] = addNodes(rdom.children[vnode], vdom.children[vnode]);
  }
  return rdom;
}

/**
  * Gets Root Element
  * @param {String} html
  * @return {Node} Root Element
  */
var getRootElement = function(html) {
  var dummy = document.createElement('div');
  dummy.innerHTML = html;
  return dummy.firstChild;
}

/**
 * Merges two Objects
 * @param {Object} obj
 * @param {Object} obj2
 * @return {Object} Merged Objects
 */
function merge(obj, obj2) {
  for (var key in obj2) {
    if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
  }
  return obj;
}

/**
 * Compiles JSX to Virtual DOM
 * @param {String} tag
 * @param {Object} attrs
 * @param {Array} children
 * @return {String} Object usable in Virtual DOM
 */
var h = function() {
  var args = Array.prototype.slice.call(arguments);
  var tag = args.shift();
  var attrs = args.shift() || {};
  var children = args;
  if(typeof children[0] === "string") {
    children[0] = createElement("#text", children[0], {}, [], defaultMeta())
  }
  return createElement(tag, children.join(""), attrs, children, defaultMeta());
};

/**
 * Sets the Elements Initial Value
 * @param {Node} el
 * @param {String} value
 */
var setInitialElementValue = function(el, value) {
  el.innerHTML = value;
}

/**
 * Does No Operation
 */
var noop = function() {

}
<commit_msg>default metadata<commit_after>/* ======= Global Utilities ======= */

/**
 * Creates a Virtual DOM Node
 * @param {String} type
 * @param {String} val
 * @param {Object} props
 * @param {Array} children
 * @param {Object} meta
 * @return {Object} Virtual DOM Node
 */
var createElement = function(type, val, props, children, meta) {
  return {
    type: type,
    val: val,
    props: props,
    children: children,
    meta: meta || {
      shouldRender: true
    }
  };
}

/**
 * Adds Nodes to Rendered Virtual DOM
 * @param {Object} rdom
 * @param {Object} vdom
 * @return {Object} Rendered Virtual DOM with Nodes
 */
var addNodes = function(rdom, vdom) {
  rdom.node = vdom.node;
  for(var vnode in rdom.children) {
    rdom.children[vnode] = addNodes(rdom.children[vnode], vdom.children[vnode]);
  }
  return rdom;
}

/**
  * Gets Root Element
  * @param {String} html
  * @return {Node} Root Element
  */
var getRootElement = function(html) {
  var dummy = document.createElement('div');
  dummy.innerHTML = html;
  return dummy.firstChild;
}

/**
 * Merges two Objects
 * @param {Object} obj
 * @param {Object} obj2
 * @return {Object} Merged Objects
 */
function merge(obj, obj2) {
  for (var key in obj2) {
    if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
  }
  return obj;
}

/**
 * Compiles JSX to Virtual DOM
 * @param {String} tag
 * @param {Object} attrs
 * @param {Array} children
 * @return {String} Object usable in Virtual DOM
 */
var h = function() {
  var args = Array.prototype.slice.call(arguments);
  var tag = args.shift();
  var attrs = args.shift() || {};
  var children = args;
  if(typeof children[0] === "string") {
    children[0] = createElement("#text", children[0], {}, [], defaultMeta())
  }
  return createElement(tag, children.join(""), attrs, children, defaultMeta());
};

/**
 * Sets the Elements Initial Value
 * @param {Node} el
 * @param {String} value
 */
var setInitialElementValue = function(el, value) {
  el.innerHTML = value;
}

/**
 * Does No Operation
 */
var noop = function() {

}
<|endoftext|>


<commit_before>/* ======= Global Utilities ======= */

/**
* Converts attributes into key-value pairs
* @param {Node} node
* @return {Object} Key-Value pairs of Attributes
*/
var extractAttrs = function(node) {
  var attrs = {};
  if(!node.attributes) return attrs;
  var rawAttrs = node.attributes;
  for(var i = 0; i < rawAttrs.length; i++) {
    attrs[rawAttrs[i].name] = rawAttrs[i].value
  }

  return attrs;
}

/**
* Compiles a template with given data
* @param {String} template
* @param {Object} data
* @return {String} Template with data rendered
*/
var compileTemplate = function(template, data) {
  var code = template,
      re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
  code.replace(re, function(match, p) {
    code = code.replace(match, "` + data." + p + " + `");
  });
  var compile = new Function("data", "var out = `" + code + "`; return out");
  var output = compile(data);
  return output;
}

/**
* Gets Root Element
* @param {String} html
* @return {Node} Root Element
*/
var getRootElement = function(html) {
  var dummy = document.createElement('div');
  dummy.innerHTML = html;
  return dummy.firstChild;
}

/**
* Merges two Objects
* @param {Object} obj
* @param {Object} obj2
* @return {Object} Merged Objects
*/
function merge(obj, obj2) {
  for (var key in obj2) {
    if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
  }
  return obj;
}

/**
* Creates an object to be used in a Virtual DOM
* @param {String} type
* @param {Array} children
* @param {String} val
* @param {Object} props
* @param {Node} node
* @return {Object} Object usable in Virtual DOM
*/
var createElement = function(type, children, val, props, node) {
  return {type: type, children: children, val: val, props: props, node: node};
}

/**
* Create Elements Recursively For all Children
* @param {Array} children
* @return {Array} Array of elements usable in Virtual DOM
*/
var recursiveChildren = function(children) {
  var recursiveChildrenArr = [];
  for(var i = 0; i < children.length; i++) {
    var child = children[i];
    recursiveChildrenArr.push(createElement(child.nodeName, recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
  }
  return recursiveChildrenArr;
}

/**
* Creates Virtual DOM
* @param {Node} node
* @return {Object} Virtual DOM
*/
var createVirtualDOM = function(node) {
  var vdom = createElement(node.nodeName, recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
  return vdom;
}

/**
 * Does No Operation
 */
var noop = function() {

}
<commit_msg>jsx to html<commit_after>/* ======= Global Utilities ======= */

/**
* Converts attributes into key-value pairs
* @param {Node} node
* @return {Object} Key-Value pairs of Attributes
*/
var extractAttrs = function(node) {
  var attrs = {};
  if(!node.attributes) return attrs;
  var rawAttrs = node.attributes;
  for(var i = 0; i < rawAttrs.length; i++) {
    attrs[rawAttrs[i].name] = rawAttrs[i].value
  }

  return attrs;
}

/**
* Compiles a template with given data
* @param {String} template
* @param {Object} data
* @return {String} Template with data rendered
*/
var compileTemplate = function(template, data) {
  var code = template,
      re = /{{([A-Za-z0-9_.()\[\]]+)}}/gi;
  code.replace(re, function(match, p) {
    code = code.replace(match, "` + data." + p + " + `");
  });
  var compile = new Function("data", "var out = `" + code + "`; return out");
  var output = compile(data);
  return output;
}

/**
* Gets Root Element
* @param {String} html
* @return {Node} Root Element
*/
var getRootElement = function(html) {
  var dummy = document.createElement('div');
  dummy.innerHTML = html;
  return dummy.firstChild;
}

/**
* Merges two Objects
* @param {Object} obj
* @param {Object} obj2
* @return {Object} Merged Objects
*/
function merge(obj, obj2) {
  for (var key in obj2) {
    if (obj2.hasOwnProperty(key)) obj[key] = obj2[key];
  }
  return obj;
}

/**
* Creates an object to be used in a Virtual DOM
* @param {String} type
* @param {Array} children
* @param {String} val
* @param {Object} props
* @param {Node} node
* @return {Object} Object usable in Virtual DOM
*/
var createElement = function(type, children, val, props, node) {
  return {type: type, children: children, val: val, props: props, node: node};
}

/**
* Create Elements Recursively For all Children
* @param {Array} children
* @return {Array} Array of elements usable in Virtual DOM
*/
var recursiveChildren = function(children) {
  var recursiveChildrenArr = [];
  for(var i = 0; i < children.length; i++) {
    var child = children[i];
    recursiveChildrenArr.push(createElement(child.nodeName, recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
  }
  return recursiveChildrenArr;
}

/**
* Creates Virtual DOM
* @param {Node} node
* @return {Object} Virtual DOM
*/
var createVirtualDOM = function(node) {
  var vdom = createElement(node.nodeName, recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
  return vdom;
}

/**
 * Compiles JSX to HTML
 * @param {String} tag
 * @param {Object} attrs
 * @param {Array} children
 * @return {String} HTML compiled from JSX
 */
var h = function(tag, attrs, children) {
	return "<" + tag + Object.keys(attrs||{}).reduce(function(total, current) {return (total||" ") + current + "='" + attrs[current] + "'"}, "") + ">" + children + "<" + tag + "/>";
};

/**
 * Compiles an elements innerHTML
 */
var noop = function() {

}

/**
 * Does No Operation
 */
var noop = function() {

}
<|endoftext|>


<commit_before>var xml = require('../lib/xmltemplates');
var sbuff = require('simple-bufferstream');
var _ = require('lodash');
var CombinedStream = require('combined-stream');
var concat = require('concat-stream');
var Q = require('q');
var sharp = require('sharp');

function err405 (res) {
	errCustom(res, 405, "MethodNotAllowed");
}
function errCustom (res, code, message) {
	res.setHeader('content-type', 'text/xml');
	var template = xml.buildError(code, message);
	res.status(code);
	res.end(template);
}

function err500 (res) {
	console.trace("500ERR");
	res.setHeader('content-type', 'text/xml');
	var template = xml.buildError('InternalError',
		'We encountered an internal error. Please try again.');
	res.status(500);
	res.end(template);
}

function handleRange (req, res, obj) {
	var range = null;
	if (req.headers['range']) {
		var r = req.headers['range'].replace('bytes=', '').split('-');
		range = {startPos: parseInt(r[0])};
		range.endPos = obj.size;
		if (r[1] !== '') {
			range.endPos = parseInt(r[1]);
		}
		res.setHeader('Content-Range', "bytes " + range.startPos+'-'+range.endPos+'/'+obj.size);
		res.status(206);
	}
	return range
}

module.exports = function objectsctrl (server, models) {

	// to promise alternative
	function getS3Object(query, failOnNull) {
		var def = Q.defer();
		models.S3Object.findOne(query, function (err, obj) {
			if (err) {
				def.reject(err);
			} else if (failOnNull && !obj) {
				def.reject(new Error("File was not found"));
			} else {
				def.resolve(obj);
			}
		});
		return def.promise;
	}
	function returnNotFoundFile(key, req, res, next) {
		var template = xml.buildKeyNotFound(key);
		res.setHeader('content-type', 'text/xml');
		res.status(404)
		res.end(template);
		return next(false);
	}
	function returnFoundFile (s3obj, req, res, next) {
		res.setHeader('Content-Type', s3obj.contentType);
		res.setHeader('Accept-Ranges', 'bytes');
		res.setHeader('Content-Length', s3obj.size);
		res.setHeader('Etag', s3obj.md5);
		res.setHeader('Last-Modified', new Date(s3obj.modifiedDate).toUTCString());
		var noneMatch = req.headers['if-none-match'];
		if (noneMatch && (noneMatch === s3obj.md5 || noneMatch === '*')) {
			res.send(304);
			return next();
		}
		var modifiedSince = req.headers['if-modified-since'];
		if (modifiedSince) {
			var time = new Date(modifiedSince);
			var modifiedDate = new Date(s3obj.modifiedDate);
			if (time >= modifiedDate) {
				res.send(304);
				return next();
			}
		}
		var range = handleRange(req, res, s3obj);
		res.status(200)
		if (req.method === 'HEAD') {
			res.end();
			return next();
		}
		var reader = s3obj.getFileReadStream(range);
		reader.pipe(res);
		return next();
	}

	/*
	* Resizes images
	*/
	function resizeFile (req, res, next) {
		var max_width = req.query.maxwidth ? Math.min(parseInt(req.query.maxwidth), server.maxresize) : null;
		var max_height = req.query.maxheight ? Math.min(parseInt(req.query.maxheight), server.maxresize) : null;
		var bucket = req.bucket.id;
		// This compose key for resized
		var resized_key = req.params.key + (max_width || 'A') + 'x' + (max_height || 'A') + '.png';
		var resized = getS3Object({
			bucket: bucket,
			key: resized_key
		}, true);

		resized.then(function (s3obj) {
			// File was found
			return returnFoundFile(s3obj, req, res, next);
		}, function (err) {
			// Not found, generating and serving!
			// Getting the original file
			var orig = getS3Object({
				bucket: bucket,
				key: req.params.key
			}, true);
			orig.fail(function (err) {
				return returnNotFoundFile(req.params.key, req, res, next);
			});
			// Original object fetched, lets compress
			orig.then(function (s3obj) {

				// Piping original file trough image conversion pipeline
				var concatStream = concat(function(imageBuffer) {
					var pipeline = sharp(imageBuffer);
					pipeline.resize(max_width, max_height)
					pipeline.max()
					pipeline.progressive()
					pipeline.toFormat('png')
					pipeline.toBuffer(function(err, outputBuffer, info) {
						if (err) {
							return returnNotFoundFile(resized_key, req, res, next);
						}
						res.setHeader('Content-Type', 'image/png');
						res.setHeader('Content-Length', info.size);
						res.end(outputBuffer);
						next();
						outputStream = sbuff(outputBuffer);
						// Save the resized image into gridfs
						streamS3Object({id: bucket}, resized_key, 'image/png', outputStream, function (err, obj) {});
					});
			});
				s3obj.getFileReadStream().pipe(concatStream);
			});
		});
}

function listMultipart (req, res, next) {
	var uploadid = req.query.uploadId;
	models.MPObject.findOne({uploadid: uploadid}, function (err, mpo) {
		next.ifError(err);
		if (!mpo) { errCustom(res, 404, "NoSuchUpload"); return next(false); }
		var template = xml.buildListMultiparts(req.bucket.name, mpo);
		res.setHeader('content-type', 'text/xml');
		res.status(200)
		res.end(template);
		return next();
	});
}

function handleMultipart (req, res, next) {
	var part = req.query.partNumber;
	var uploadid = req.query.uploadId;
	models.MPObject.findOne({uploadid: uploadid}, function (err, mpo) {
		next.ifError(err);
		if (!mpo) { errCustom(res, 404, "NoSuchUpload"); return next(false); }
		console.log("MPO found...");
		mpo.removePart(part);
		var wstream = mpo.getFileWriteStream(part);
			//console.log(req);

			req.pipe(wstream);
			wstream.on('close', function (file) {
				console.log(file);
				mpo.parts = _.filter(mpo.parts, function (v, i, c) {
					return v.part !== part;
				});
				mpo.parts.push({uuid: file._id, part: part, md5:file.md5, size: file.length});
				mpo.save(function (err, mpo) {
					next.ifError(err);
					res.setHeader('ETag', '"'+file.md5+'"');
					res.send(200);
					return next();
				});
			});
		});
}

function mpInitiate (req, res, next) {
	req.params.key = req.params[0];
	if (req.query.uploads !== undefined) {
		console.log("Initiating MP upload")
		var mp = models.MPObject({bucket: req.bucket.id, key: req.params.key});
		mp.generateKey();
		mp.save(function (err, mp) {
			var template = xml.buildInitiateMultipartUpload(req.bucket.name, mp);
			res.setHeader('content-type', 'text/xml');
			res.status(200)
			res.end(template);
		});
	} else if (req.query.uploadId !== undefined) {
			// Completed mp upload
			models.MPObject.findOne({uploadid: req.query.uploadId}, function (err, mpo) {
				next.ifError(err);
				var combinedStream = CombinedStream.create();
				var streams = mpo.parts.sort(function(parta, partb) {
					return parta.part - partb.part;
				}).map(function (part) {
					return mpo.getFileReadStream(part.part)
				}).forEach(function (stream) {
					combinedStream.append(stream);
				});
				streamS3Object(req.bucket, req.params.key, mpo.contentType, combinedStream,
					function (err, obj){
						if (err) { err500(res); return next(false); }
						var template = xml.buildCompleteMultipartUpload(req.bucket.name, obj);
						res.setHeader('content-type', 'text/xml');
						res.status(200)
						res.send(template);
					});
			});
		} else {
			console.log(req.query)
			err405(res);
			return next(false);
		}
	}

	function getObject (req, res, next) {
		req.params.key = req.params[0];
		var acl = req.query.acl;
		var uploadid = req.query.uploadId;
		var max_width = req.query.maxwidth;
		var max_height = req.query.maxheight;
		if (uploadid !== undefined) {
			return listMultipart(req, res, next);
		}
		if (max_height !== undefined || max_width !== undefined) {
			return resizeFile(req, res, next);
		}
		if (acl !== undefined) {
			res.setHeader('content-type', 'text/xml');
			var template = xml.buildAcl();
			res.status(200).end(template);
			return next();
		}
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			if (!obj) {
				var template = xml.buildKeyNotFound(req.params.key);
				res.setHeader('content-type', 'text/xml');
				res.status(404)
				res.end(template);
				return next(false);
			}
			return returnFoundFile(obj, req, res, next);
		});
	}

	function deleteObject (req, res, next) {
		req.params.key = req.params[0];
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			next.ifError(err);
			if (obj === null) {
				var template = xml.buildKeyNotFound(req.params.key);
				res.setHeader('content-type', 'text/xml');
				res.status(404)
				res.end(template);
				return next();
			}
			obj.remove(function (err, obj) {
				next.ifError(err);
				res.send(204);
				return next();
			});
		});
	}

	function removeDBObject (req, next) {
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			next.ifError(err);
			if (obj === null) { return; }
			obj.remove(function (err, obj) {
				next.ifError(err);
			});
		});
	}

	function streamS3Object (bucket, key, contentType, readStream, cb) {

		var obj = models.S3Object({
			bucket: bucket.id,
			key: key,
			contentType: contentType
		});
		obj.generateKey();
		var wstream = obj.getFileWriteStream(contentType);
	//console.log(req.data.length);

	readStream.pipe(wstream);

	wstream.on('close', function (file) {
		obj.md5 = file.md5;
		obj.size = file.length;

		obj.save(cb);
	});
}

function putObject (req, res, next) {
	req.params.key = req.params[0];
	removeDBObject(req, next);

	// Multipart upload
	if (req.query.uploadId !== undefined
		&& req.query.partNumber !== undefined) {
		console.log("Uploading part " + req.query.uploadId + "; " + req.query.partNumber);
		return handleMultipart(req, res, next);
	}
	/*
	if (!req.body){
		err500(res);
		return next();
	}
	var reader = sbuff(req.body);
	*/
	streamS3Object(req.bucket, req.params.key, req.headers['content-type'], req,
	function (err, obj) {
		if (err) {
				// 500
				err500(res);
				return next();
			}
			res.setHeader('ETag', '"'+obj.md5+'"');
			res.send(200);
			return next();
		});
}

return {
	getObject: getObject,
	putObject: putObject,
	mpInitiate: mpInitiate,
	deleteObject: deleteObject
};
};
<commit_msg>Update objects.js<commit_after>var xml = require('../lib/xmltemplates');
var sbuff = require('simple-bufferstream');
var _ = require('lodash');
var CombinedStream = require('combined-stream');
var concat = require('concat-stream');
var Q = require('q');
var sharp = require('sharp');

function err405 (res) {
	errCustom(res, 405, "MethodNotAllowed");
}
function errCustom (res, code, message) {
	res.setHeader('content-type', 'text/xml');
	var template = xml.buildError(code, message);
	res.status(code);
	res.end(template);
}

function err500 (res) {
	console.trace("500ERR");
	res.setHeader('content-type', 'text/xml');
	var template = xml.buildError('InternalError',
		'We encountered an internal error. Please try again.');
	res.status(500);
	res.end(template);
}

function handleRange (req, res, obj) {
	var range = null;
	if (req.headers['range']) {
		var r = req.headers['range'].replace('bytes=', '').split('-');
		range = {startPos: parseInt(r[0])};
		range.endPos = obj.size;
		if (r[1] !== '') {
			range.endPos = parseInt(r[1]);
		}
		res.setHeader('Content-Range', "bytes " + range.startPos+'-'+range.endPos+'/'+obj.size);
		res.status(206);
	}
	return range
}

module.exports = function objectsctrl (server, models) {

	// to promise alternative
	function getS3Object(query, failOnNull) {
		var def = Q.defer();
		models.S3Object.findOne(query, function (err, obj) {
			if (err) {
				def.reject(err);
			} else if (failOnNull && !obj) {
				def.reject(new Error("File was not found"));
			} else {
				def.resolve(obj);
			}
		});
		return def.promise;
	}
	function returnNotFoundFile(key, req, res, next) {
		var template = xml.buildKeyNotFound(key);
		res.setHeader('content-type', 'text/xml');
		res.status(404)
		res.end(template);
		return next(false);
	}
	function returnFoundFile (s3obj, req, res, next) {
		console.log("Return found file", s3obj);
		res.setHeader('Content-Type', s3obj.contentType);
		res.setHeader('Accept-Ranges', 'bytes');
		res.setHeader('Content-Length', s3obj.size);
		res.setHeader('Etag', s3obj.md5);
		res.setHeader('Last-Modified', new Date(s3obj.modifiedDate).toUTCString());
		var noneMatch = req.headers['if-none-match'];
		if (noneMatch && (noneMatch === s3obj.md5 || noneMatch === '*')) {
			res.send(304);
			return next();
		}
		var modifiedSince = req.headers['if-modified-since'];
		if (modifiedSince) {
			var time = new Date(modifiedSince);
			var modifiedDate = new Date(s3obj.modifiedDate);
			if (time >= modifiedDate) {
				res.send(304);
				return next();
			}
		}
		var range = handleRange(req, res, s3obj);
		res.status(200)
		if (req.method === 'HEAD') {
			res.end();
			return next();
		}
		var reader = s3obj.getFileReadStream(range);
		reader.pipe(res);
		return next();
	}

	/*
	* Resizes images
	*/
	function resizeFile (req, res, next) {
		var max_width = req.query.maxwidth ? Math.min(parseInt(req.query.maxwidth), server.maxresize) : null;
		var max_height = req.query.maxheight ? Math.min(parseInt(req.query.maxheight), server.maxresize) : null;
		var bucket = req.bucket.id;
		// This compose key for resized
		var resized_key = req.params.key + (max_width || 'A') + 'x' + (max_height || 'A') + '.png';
		var resized = getS3Object({
			bucket: bucket,
			key: resized_key
		}, true);

		resized.then(function (s3obj) {
			// File was found
			return returnFoundFile(s3obj, req, res, next);
		}, function (err) {
			// Not found, generating and serving!
			// Getting the original file
			var orig = getS3Object({
				bucket: bucket,
				key: req.params.key
			}, true);
			orig.fail(function (err) {
				return returnNotFoundFile(req.params.key, req, res, next);
			});
			// Original object fetched, lets compress
			orig.then(function (s3obj) {

				// Piping original file trough image conversion pipeline
				var concatStream = concat(function(imageBuffer) {
					var pipeline = sharp(imageBuffer);
					pipeline.resize(max_width, max_height)
					pipeline.max()
					pipeline.progressive()
					pipeline.toFormat('png')
					pipeline.toBuffer(function(err, outputBuffer, info) {
						if (err) {
							return returnNotFoundFile(resized_key, req, res, next);
						}
						res.setHeader('Content-Type', 'image/png');
						res.setHeader('Content-Length', info.size);
						res.end(outputBuffer);
						next();
						outputStream = sbuff(outputBuffer);
						// Save the resized image into gridfs
						streamS3Object({id: bucket}, resized_key, 'image/png', outputStream, function (err, obj) {});
					});
			});
				s3obj.getFileReadStream().pipe(concatStream);
			});
		});
}

function listMultipart (req, res, next) {
	var uploadid = req.query.uploadId;
	models.MPObject.findOne({uploadid: uploadid}, function (err, mpo) {
		next.ifError(err);
		if (!mpo) { errCustom(res, 404, "NoSuchUpload"); return next(false); }
		var template = xml.buildListMultiparts(req.bucket.name, mpo);
		res.setHeader('content-type', 'text/xml');
		res.status(200)
		res.end(template);
		return next();
	});
}

function handleMultipart (req, res, next) {
	var part = req.query.partNumber;
	var uploadid = req.query.uploadId;
	models.MPObject.findOne({uploadid: uploadid}, function (err, mpo) {
		next.ifError(err);
		if (!mpo) { errCustom(res, 404, "NoSuchUpload"); return next(false); }
		console.log("MPO found...");
		mpo.removePart(part);
		var wstream = mpo.getFileWriteStream(part);
			//console.log(req);

			req.pipe(wstream);
			wstream.on('close', function (file) {
				console.log(file);
				mpo.parts = _.filter(mpo.parts, function (v, i, c) {
					return v.part !== part;
				});
				mpo.parts.push({uuid: file._id, part: part, md5:file.md5, size: file.length});
				mpo.save(function (err, mpo) {
					next.ifError(err);
					res.setHeader('ETag', '"'+file.md5+'"');
					res.send(200);
					return next();
				});
			});
		});
}

function mpInitiate (req, res, next) {
	req.params.key = req.params[0];
	if (req.query.uploads !== undefined) {
		console.log("Initiating MP upload")
		var mp = models.MPObject({bucket: req.bucket.id, key: req.params.key});
		mp.generateKey();
		mp.save(function (err, mp) {
			var template = xml.buildInitiateMultipartUpload(req.bucket.name, mp);
			res.setHeader('content-type', 'text/xml');
			res.status(200)
			res.end(template);
		});
	} else if (req.query.uploadId !== undefined) {
			// Completed mp upload
			models.MPObject.findOne({uploadid: req.query.uploadId}, function (err, mpo) {
				next.ifError(err);
				var combinedStream = CombinedStream.create();
				var streams = mpo.parts.sort(function(parta, partb) {
					return parta.part - partb.part;
				}).map(function (part) {
					return mpo.getFileReadStream(part.part)
				}).forEach(function (stream) {
					combinedStream.append(stream);
				});
				streamS3Object(req.bucket, req.params.key, mpo.contentType, combinedStream,
					function (err, obj){
						if (err) { err500(res); return next(false); }
						var template = xml.buildCompleteMultipartUpload(req.bucket.name, obj);
						res.setHeader('content-type', 'text/xml');
						res.status(200)
						res.send(template);
					});
			});
		} else {
			console.log(req.query)
			err405(res);
			return next(false);
		}
	}

	function getObject (req, res, next) {
		req.params.key = req.params[0];
		var acl = req.query.acl;
		var uploadid = req.query.uploadId;
		var max_width = req.query.maxwidth;
		var max_height = req.query.maxheight;
		if (uploadid !== undefined) {
			return listMultipart(req, res, next);
		}
		if (max_height !== undefined || max_width !== undefined) {
			return resizeFile(req, res, next);
		}
		if (acl !== undefined) {
			res.setHeader('content-type', 'text/xml');
			var template = xml.buildAcl();
			res.status(200).end(template);
			return next();
		}
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			if (!obj) {
				var template = xml.buildKeyNotFound(req.params.key);
				res.setHeader('content-type', 'text/xml');
				res.status(404)
				res.end(template);
				return next(false);
			}
			return returnFoundFile(obj, req, res, next);
		});
	}

	function deleteObject (req, res, next) {
		req.params.key = req.params[0];
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			next.ifError(err);
			if (obj === null) {
				var template = xml.buildKeyNotFound(req.params.key);
				res.setHeader('content-type', 'text/xml');
				res.status(404)
				res.end(template);
				return next();
			}
			obj.remove(function (err, obj) {
				next.ifError(err);
				res.send(204);
				return next();
			});
		});
	}

	function removeDBObject (req, next) {
		models.S3Object.findOne({
			bucket: req.bucket.id,
			key: req.params.key
		}, function (err, obj) {
			next.ifError(err);
			if (obj === null) { return; }
			obj.remove(function (err, obj) {
				next.ifError(err);
			});
		});
	}

	function streamS3Object (bucket, key, contentType, readStream, cb) {

		var obj = models.S3Object({
			bucket: bucket.id,
			key: key,
			contentType: contentType
		});
		obj.generateKey();
		var wstream = obj.getFileWriteStream(contentType);
	//console.log(req.data.length);

	readStream.pipe(wstream);

	wstream.on('close', function (file) {
		obj.md5 = file.md5;
		obj.size = file.length;

		obj.save(cb);
	});
}

function putObject (req, res, next) {
	req.params.key = req.params[0];
	removeDBObject(req, next);

	// Multipart upload
	if (req.query.uploadId !== undefined
		&& req.query.partNumber !== undefined) {
		console.log("Uploading part " + req.query.uploadId + "; " + req.query.partNumber);
		return handleMultipart(req, res, next);
	}
	/*
	if (!req.body){
		err500(res);
		return next();
	}
	var reader = sbuff(req.body);
	*/
	console.log(req.bucket, req.params.key);
	streamS3Object(req.bucket, req.params.key, req.headers['content-type'], req,
	function (err, obj) {
		if (err) {
				// 500
				err500(res);
				return next();
			}
			res.setHeader('ETag', '"'+obj.md5+'"');
			res.send(200);
			return next();
		});
}

return {
	getObject: getObject,
	putObject: putObject,
	mpInitiate: mpInitiate,
	deleteObject: deleteObject
};
};
<|endoftext|>


<commit_before>/**
 * Created by xiawanqiang on 14-5-1.
 */

/**
 * YQF, define QP active object framework in YUI
 */
YUI.add('yqf', function(Y) {
    var yqf = Y.namespace('yqf'),
        RET_HANDLED = 0,
        RET_IGNORED = 1,
        RET_TRANSFER = 2,
        RET_SUPER = 3,
        RET_UNHANDLED = 4,
        QF_TICK_RATE = 10,
        top = null;

    var ignored = function() {
        return RET_IGNORED;
    };

    var handled = function() {
        return RET_HANDLED;
    };

    var unhandled = function() {
        return RET_UNHANDLED;
    };

    /*
     * handler required, other optional
     */
    var QState = function(handler, parent, entry, exit, init) {
        this.handler = handler;

        if (typeof parent === 'undefined') {
            this.parent = top;
        } else {
            this.parent = parent;
        }

        this.entry = entry || handled;
        this.exit = exit || handled;
        this.init = init || handled;
    };

    top = new QState(ignored, null);

    /*
     * signal required, other optional
     */
    var QEvent = function(signal, data, sender) {
        this.signal = signal;

        if (typeof data === 'undefined') {
            this.data = null;
        } else {
            this.data = data;
        }

        if (typeof sender === 'undefined') {
            this.sender = null;
        } else {
            this.sender = sender;
        }
    };

    /*
     * ao, signal, all required
     */
    var QTimeEvent = function(ao, signal) {
        this.lastTick = Date.now();
        this.activeObject = ao;
        this.signal = signal;
        this.interval = 0;
        this.repeat = false;
    };

    /*
     * QHsm, defines the Hierarchical State Machine (HSM)
     */
    var QHsm = function(initial) {
        var state = top,
            target = initial;

        var findLca = function(p, q) {
            var ps = [],
                qs = [],
                t = top;

            ps.push(p);
            qs.push(q);

            while (p !== top) {
                p = p.parent;
                ps.push(p);
            }

            while(q !== top) {
                q = q.parent;
                qs.push(q);
            }

            while (ps.length > 0 && qs.length > 0) {
                p = ps.pop();
                q = qs.pop();
                if (p === q) {
                    t = p;
                } else {
                    break;
                }
            }

            return t;
        };

        this.transfer = function(t) {
            target = t;
            return RET_TRANSFER;
        };

        this.init = function(e) {
            var s = state,
                t = target,
                i = 0,
                r = RET_HANDLED,
                path = [];

            r = t.handler(e);

            do {                             // drill into the target...
                path = [];
                path.push(target);
                t = target.parent;
                while (s !== t) {
                    path.push(t);
                    t = t.parent;
                }

                for (i = path.length - 1; i >= 0; i--) {
                    path[i].entry();
                }

                s = target;
            } while (s.init() === RET_TRANSFER);

            state = s;                            // change the current active state
            target  = s;                           // mark the configuration as stable
        };

        //
        this.dispatch = function(e) {
            var s = state,
                t = target,
                p = top,
                r = RET_HANDLED,
                entries = [],
                exits = [];

            r = t.handler(e);
            while (r === RET_UNHANDLED) {
                t = t.parent;
                r = t.handler(e);
            }

            if (r === RET_TRANSFER) {
                // current -> transfer source, exit
                while (s !== t) {
                    exits.push(s);
                    s = s.parent;
                }

                if (s === target) {	// self transfer
                    exits.push(s);
                    entries.push(s);
                } else {
                    // Lca p
                    p = findLca(s, target);

                    // source -> p, exit
                    while (s !== p) {
                        exits.push(s);
                        s = s.parent;
                    }

                    // target -> p, entries
                    s = target;
                    while (s !== p) {
                        entries.push(s);
                        s = s.parent;
                    }
                }

                // do exists
                while (exits.length > 0) {
                    s = exits.shift();
                    s.exit();
                }

                // do entries
                while (entries.length > 0) {
                    t = entries.pop();
                    t.entry();
                }

                // drill in target, init
                t = target;
                while (t.init() === RET_TRANSFER) {
                    entries = [];
                    entries.push(target);
                    s = target.parent;
                    while (s !== t) {
                        entries.push(s);
                        s = s.parent;
                    }

                    while (entries.length > 0) {
                        entries.pop().entry();
                    }

                    t = target;
                }

                state = t;
                target = t;
            }
        };

        this.isIn = function(s) {
            var p = state;

            while (p !== top) {
                if (p === s) {
                    return true;
                }
                p = p.parent;
            }

            return false;
        };
    };

    QHsm.top = top;
    QHsm.ignored = ignored;
    QHsm.handled = handled;
    QHsm.unhandled = unhandled;

    /**
     * QActive, defines the active object
     */
    var QActive = function(initial) {
        var eventQueue = [],
            deferQueue = [];

        this.postFifo = function(e) {
            eventQueue.push(e);
        };

        this.postLifo = function(e) {
            eventQueue.unshift(e);
        };

        this.getEvent = function() {
            var result = null;

            if (eventQueue.length !== 0) {
                result = eventQueue.shift();
            }

            return result;
        };

        this.defer = function(e) {
            deferQueue.push(e);
        };

        this.recall = function() {
            var i = 0,
                e = null,
                length = deferQueue.length;

            for (i = 0; i < length; i++) {
                e = deferQueue.shift();
                eventQueue.push(e);
            }

            return length > 0;
        };

        this.priority = 0;
        QHsm.call(this, initial);
    };

    Y.extend(QActive, QHsm);

    var indexOf = function(array, item) {
        var i = 0,
            length = array.length;

        for (i = 0; i < length; i++) {
            if (array[i] === item) {
                return i;
            }
        }

        return -1;
    };

    /**
     * QF, defines the active object framework
     */
    var QF = {
        subscribes: {},

        activeObjects: [],

        timeEvents: [],

        timerId: 0,

        tickRate: QF_TICK_RATE,

        init: function(tick) {
            QF.subscribes = {};
            QF.activeObjects = [];
            QF.timeEvents = [];

            if (!Y.Lang.isNumber(tick)) {
                QF.tickRate = QF_TICK_RATE;
            } else {
                QF.tickRate = tick;
            }

            QF.timerId = 0;
        },

        run: function() {
            QF.onStartup();
            QF.timerId = setInterval(QF.tick, QF.tickRate);
        },

        startActive: function(ao, priority) {
            if (indexOf(QF.activeObjects, ao) === -1) {
                ao.priority = priority;
                QF.activeObjects.push(ao);
                QF.activeObjects.sort(function(a, b) {
                    return a.priority - b.priority;
                });
                ao.init(null);
            }
        },

        stopActive: function(ao) {
            var pos = indexOf(QF.activeObjects, ao);
            if (pos !== -1) {
                QF.activeObjects.splice(pos, 1);
            }
        },

        arm: function(te, interval, repeat) {
            if (indexOf(QF.timeEvents, te) === -1) {
                te.interval = interval;
                te.repeat = repeat;
                te.lastTick = Date.now();
                QF.timeEvents.push(te);
            }
        },

        disarm: function(te) {
            var pos = indexOf(QF.timeEvents, te);
            if (pos !== -1) {
                QF.timeEvents.splice(pos, 1);
            }
        },

        subscribe: function(ao, signal) {
            var subs = QF.subscribes[signal];
            if (!subs) {
                QF.subscribes[signal] = [];
                subs = QF.subscribes[signal];
            }

            if (indexOf(subs, ao) === -1) {
                subs.push(ao);
            }
        },

        unsubscribe: function(ao, signal) {
            var subs = QF.subscribes[signal],
                pos = 0;

            if (!subs) {
                return;
            }

            pos = indexOf(subs, ao);
            if (pos !== -1) {
                subs.splice(pos, 1);
            }
        },

        unsubscribeAll: function(ao) {
            var pos = 0,
                p,
                subs = [];

            for (p in QF.subscribes) {
                if (QF.subscribes.hasOwnProperty(p) &&
                    QF.subscribes[p] instanceof Array ) {
                    subs = QF.subscribes[p];
                    pos = indexOf(subs, ao);
                    if (pos !== -1) {
                        subs.splice(pos, 1);
                    }
                }
            }
        },

        publish: function(e) {
            var i = 0,
                length = 0,
                subs = QF.subscribes[e.signal];

            if (!subs) {
                return;
            }

            for (i = 0, length = subs.length; i < length; i++) {
                subs[i].postFifo(e);
            }
        },

        tick: function() {
            var i = 0,
                length = QF.timeEvents.length,
                te = null,
                timeNow = Date.now();

            for (i = length - 1; i >= 0; i--) {
                te = QF.timeEvents[i];
                if ((timeNow - te.lastTick) >= te.interval) {
                    te.activeObject.postFifo(new QEvent(te.signal, null, te.activeObject));
                    if (te.repeat) {
                        te.lastTick = timeNow;
                    } else {
                        QF.timeEvents.splice(i, 1);	// remove
                    }
                }
            }

            QF.schedule();
        },

        schedule: function() {
            var i = 0,
                e = null,
                ao = null,
                length = QF.activeObjects.length;

            for (i = length - 1; i >= 0; i--) {
                ao = QF.activeObjects[i];
                e = ao.getEvent();
                while (e !== null) {
                    ao.dispatch(e);
                    e = ao.getEvent();
                }
            }
        },

        onStartup: function() {
            console.log('QF Startup!');
        },

        stop: function() {
            QF.onCleanup();
            clearInterval(QF.timerId);
        },

        onCleanup: function() {
            console.log('QF Cleanup');
        }
    };

    Y.yqf.QF = QF;
    Y.yqf.QState = QState;
    Y.yqf.QHsm = QHsm;
    Y.yqf.QActive = QActive;
    Y.yqf.QEvent = QEvent;
    Y.yqf.QTimeEvent = QTimeEvent;
}, '0.0.1', ['base']);<commit_msg>use setTimeout to schedule running<commit_after>/**
 * Created by xiawanqiang on 14-5-1.
 */

/**
 * YQF, define QP active object framework in YUI
 */
YUI.add('yqf', function(Y) {
    var yqf = Y.namespace('yqf'),
        RET_HANDLED = 0,
        RET_IGNORED = 1,
        RET_TRANSFER = 2,
        RET_SUPER = 3,
        RET_UNHANDLED = 4,
        QF_TICK_RATE = 10,
        top = null;

    var ignored = function() {
        return RET_IGNORED;
    };

    var handled = function() {
        return RET_HANDLED;
    };

    var unhandled = function() {
        return RET_UNHANDLED;
    };

    /*
     * handler required, other optional
     */
    var QState = function(handler, parent, entry, exit, init) {
        this.handler = handler;

        if (typeof parent === 'undefined') {
            this.parent = top;
        } else {
            this.parent = parent;
        }

        this.entry = entry || handled;
        this.exit = exit || handled;
        this.init = init || handled;
    };

    top = new QState(ignored, null);

    /*
     * signal required, other optional
     */
    var QEvent = function(signal, data, sender) {
        this.signal = signal;

        if (typeof data === 'undefined') {
            this.data = null;
        } else {
            this.data = data;
        }

        if (typeof sender === 'undefined') {
            this.sender = null;
        } else {
            this.sender = sender;
        }
    };

    /*
     * ao, signal, all required
     */
    var QTimeEvent = function(ao, signal) {
        this.lastTick = Date.now();
        this.activeObject = ao;
        this.signal = signal;
        this.interval = 0;
        this.repeat = false;
    };

    /*
     * QHsm, defines the Hierarchical State Machine (HSM)
     */
    var QHsm = function(initial) {
        var state = top,
            target = initial;

        var findLca = function(p, q) {
            var ps = [],
                qs = [],
                t = top;

            ps.push(p);
            qs.push(q);

            while (p !== top) {
                p = p.parent;
                ps.push(p);
            }

            while(q !== top) {
                q = q.parent;
                qs.push(q);
            }

            while (ps.length > 0 && qs.length > 0) {
                p = ps.pop();
                q = qs.pop();
                if (p === q) {
                    t = p;
                } else {
                    break;
                }
            }

            return t;
        };

        this.transfer = function(t) {
            target = t;
            return RET_TRANSFER;
        };

        this.init = function(e) {
            var s = state,
                t = target,
                i = 0,
                r = RET_HANDLED,
                path = [];

            r = t.handler(e);

            do {                             // drill into the target...
                path = [];
                path.push(target);
                t = target.parent;
                while (s !== t) {
                    path.push(t);
                    t = t.parent;
                }

                for (i = path.length - 1; i >= 0; i--) {
                    path[i].entry();
                }

                s = target;
            } while (s.init() === RET_TRANSFER);

            state = s;                            // change the current active state
            target  = s;                           // mark the configuration as stable
        };

        //
        this.dispatch = function(e) {
            var s = state,
                t = target,
                p = top,
                r = RET_HANDLED,
                entries = [],
                exits = [];

            r = t.handler(e);
            while (r === RET_UNHANDLED) {
                t = t.parent;
                r = t.handler(e);
            }

            if (r === RET_TRANSFER) {
                // current -> transfer source, exit
                while (s !== t) {
                    exits.push(s);
                    s = s.parent;
                }

                if (s === target) {	// self transfer
                    exits.push(s);
                    entries.push(s);
                } else {
                    // Lca p
                    p = findLca(s, target);

                    // source -> p, exit
                    while (s !== p) {
                        exits.push(s);
                        s = s.parent;
                    }

                    // target -> p, entries
                    s = target;
                    while (s !== p) {
                        entries.push(s);
                        s = s.parent;
                    }
                }

                // do exists
                while (exits.length > 0) {
                    s = exits.shift();
                    s.exit();
                }

                // do entries
                while (entries.length > 0) {
                    t = entries.pop();
                    t.entry();
                }

                // drill in target, init
                t = target;
                while (t.init() === RET_TRANSFER) {
                    entries = [];
                    entries.push(target);
                    s = target.parent;
                    while (s !== t) {
                        entries.push(s);
                        s = s.parent;
                    }

                    while (entries.length > 0) {
                        entries.pop().entry();
                    }

                    t = target;
                }

                state = t;
                target = t;
            }
        };

        this.isIn = function(s) {
            var p = state;

            while (p !== top) {
                if (p === s) {
                    return true;
                }
                p = p.parent;
            }

            return false;
        };
    };

    QHsm.top = top;
    QHsm.ignored = ignored;
    QHsm.handled = handled;
    QHsm.unhandled = unhandled;

    /**
     * QActive, defines the active object
     */
    var QActive = function(initial) {
        var eventQueue = [],
            deferQueue = [];

        this.run = function() {
            var e = this.getEvent();

            if (e != null) {
                this.dispatch(e);
            }
        };

        this.schedule = function() {
            setTimeout(function() {
                this.run();
            }.bind(this), 0);
        };
        this.postFifo = function(e) {
            eventQueue.push(e);
            this.schedule();
        };

        this.postLifo = function(e) {
            eventQueue.unshift(e);
            this.schedule();
        };

        this.getEvent = function() {
            var result = null;

            if (eventQueue.length !== 0) {
                result = eventQueue.shift();
            }

            return result;
        };

        this.defer = function(e) {
            deferQueue.push(e);
        };

        this.recall = function() {
            var i = 0,
                e = null,
                length = deferQueue.length;

            for (i = 0; i < length; i++) {
                e = deferQueue.shift();
                eventQueue.push(e);
                this.schedule();
            }

            return length > 0;
        };

        this.priority = 0;
        QHsm.call(this, initial);
    };

    Y.extend(QActive, QHsm);

    var indexOf = function(array, item) {
        var i = 0,
            length = array.length;

        for (i = 0; i < length; i++) {
            if (array[i] === item) {
                return i;
            }
        }

        return -1;
    };

    /**
     * QF, defines the active object framework
     */
    var QF = {
        subscribes: {},

        activeObjects: [],

        timeEvents: [],

        timerId: 0,

        tickRate: QF_TICK_RATE,

        init: function(tick) {
            QF.subscribes = {};
            QF.activeObjects = [];
            QF.timeEvents = [];

            if (!Y.Lang.isNumber(tick)) {
                QF.tickRate = QF_TICK_RATE;
            } else {
                QF.tickRate = tick;
            }

            QF.timerId = 0;
        },

        run: function() {
            QF.onStartup();
            QF.timerId = setInterval(QF.tick, QF.tickRate);
        },

        startActive: function(ao, priority) {
            if (indexOf(QF.activeObjects, ao) === -1) {
                ao.priority = priority;
                QF.activeObjects.push(ao);
                QF.activeObjects.sort(function(a, b) {
                    return a.priority - b.priority;
                });
                ao.init(null);
            }
        },

        stopActive: function(ao) {
            var pos = indexOf(QF.activeObjects, ao);
            if (pos !== -1) {
                QF.activeObjects.splice(pos, 1);
            }
        },

        arm: function(te, interval, repeat) {
            if (indexOf(QF.timeEvents, te) === -1) {
                te.interval = interval;
                te.repeat = repeat;
                te.lastTick = Date.now();
                QF.timeEvents.push(te);
            }
        },

        disarm: function(te) {
            var pos = indexOf(QF.timeEvents, te);
            if (pos !== -1) {
                QF.timeEvents.splice(pos, 1);
            }
        },

        subscribe: function(ao, signal) {
            var subs = QF.subscribes[signal];
            if (!subs) {
                QF.subscribes[signal] = [];
                subs = QF.subscribes[signal];
            }

            if (indexOf(subs, ao) === -1) {
                subs.push(ao);
            }
        },

        unsubscribe: function(ao, signal) {
            var subs = QF.subscribes[signal],
                pos = 0;

            if (!subs) {
                return;
            }

            pos = indexOf(subs, ao);
            if (pos !== -1) {
                subs.splice(pos, 1);
            }
        },

        unsubscribeAll: function(ao) {
            var pos = 0,
                p,
                subs = [];

            for (p in QF.subscribes) {
                if (QF.subscribes.hasOwnProperty(p) &&
                    QF.subscribes[p] instanceof Array ) {
                    subs = QF.subscribes[p];
                    pos = indexOf(subs, ao);
                    if (pos !== -1) {
                        subs.splice(pos, 1);
                    }
                }
            }
        },

        publish: function(e) {
            var i = 0,
                length = 0,
                subs = QF.subscribes[e.signal];

            if (!subs) {
                return;
            }

            for (i = 0, length = subs.length; i < length; i++) {
                subs[i].postFifo(e);
            }
        },

        tick: function() {
            var i = 0,
                length = QF.timeEvents.length,
                te = null,
                timeNow = Date.now();

            for (i = length - 1; i >= 0; i--) {
                te = QF.timeEvents[i];
                if ((timeNow - te.lastTick) >= te.interval) {
                    te.activeObject.postFifo(new QEvent(te.signal, null, te.activeObject));
                    if (te.repeat) {
                        te.lastTick = timeNow;
                    } else {
                        QF.timeEvents.splice(i, 1);	// remove
                    }
                }
            }

            // Use setTimeout shcedule running, this call is not needed.
            //QF.schedule();
        },

        schedule: function() {
            var i = 0,
                e = null,
                ao = null,
                length = QF.activeObjects.length;

            for (i = length - 1; i >= 0; i--) {
                ao = QF.activeObjects[i];
                e = ao.getEvent();
                while (e !== null) {
                    ao.dispatch(e);
                    e = ao.getEvent();
                }
            }
        },

        onStartup: function() {
            console.log('QF Startup!');
        },

        stop: function() {
            QF.onCleanup();
            clearInterval(QF.timerId);
        },

        onCleanup: function() {
            console.log('QF Cleanup');
        }
    };

    Y.yqf.QF = QF;
    Y.yqf.QState = QState;
    Y.yqf.QHsm = QHsm;
    Y.yqf.QActive = QActive;
    Y.yqf.QEvent = QEvent;
    Y.yqf.QTimeEvent = QTimeEvent;
}, '0.0.1', ['base']);<|endoftext|>


<commit_before>var ProductProvider = require('../business/logic/productprovider');
var jwt = require('jsonwebtoken');
var User = require('../models/User');

/**
 * GET product/:pId
 */
exports.productGet = function(req, res) {
    var token = (req.headers.authorization && req.headers.authorization.split(' ')[1]) || req.cookies.token;
    var tokenParams = jwt.decode(token, process.env.TOKEN_SECRET);
    var userId = undefined;
    if(tokenParams){
        userId = tokenParams.sub;
    }

    ProductProvider.getProduct(req.params.pId)
        .then(function(product){
            if(userId){
                console.log('the user id is ' + userId);
                User.findById(userId , function(err, user) {
                    if(user){
                        markProblematicIngredients(user,product);
                    }
                    res.send(product);
                });
                //mark problematic ingredients
            }else{
                res.send(product);
            }
        },function(err){
            console.error(err);
            res.send(err);
        });
};

function markProblematicIngredients(user,product){
    if(user && product){
        product.ingredient_analysis = [];
        if(user.allergies.length == 0){
            for(let ingredientIndex = 0; ingredientIndex <  product.ingredients.length; ingredientIndex++){
                product.ingredient_analysis.push({
                    name: product.ingredients[ingredientIndex],
                    analysis: 'UNKNOWN'
                });
            }
        }else {
            for (let ingredientIndex = 0; ingredientIndex < product.ingredients.length; ingredientIndex++) {
                let isSensetive = false;
                for (let allergyIndex = 0; allergyIndex < user.allergies.length; allergyIndex++) {
                    if (user.allergies[allergyIndex].originalObject.compound.toLowerCase().indexOf(product.ingredients[ingredientIndex].toLowerCase()) > -1) {
                        product.ingredient_analysis.push({
                            name: product.ingredients[ingredientIndex],
                            analysis: 'SENSITIVE'
                        });
                        isSensetive = true;
                        break;
                    }
                }
                if(!isSensetive) {
                    product.ingredient_analysis.push({
                        name: product.ingredients[ingredientIndex],
                        analysis: 'NOT_SENSITIVE'
                    })
                }
            }
        }

        product._doc.ingredient_analysis = product.ingredient_analysis;
    }
}<commit_msg>save search history<commit_after>var ProductProvider = require('../business/logic/productprovider');
var jwt = require('jsonwebtoken');
var User = require('../models/User');

/**
 * GET product/:pId
 */
exports.productGet = function(req, res) {
    var token = (req.headers.authorization && req.headers.authorization.split(' ')[1]) || req.cookies.token;
    var tokenParams = jwt.decode(token, process.env.TOKEN_SECRET);
    var userId = undefined;
    if(tokenParams){
        userId = tokenParams.sub;
    }

    ProductProvider.getProduct(req.params.pId)
        .then(function(product){
            if(userId){
                console.log('the user id is ' + userId);
                User.findById(userId , function(err, user) {
                    if(user){
                        markProblematicIngredients(user,product);
                        saveUserSearch(user,product);
                    }
                    res.send(product);
                });
                //mark problematic ingredients
            }else{
                res.send(product);
            }
        },function(err){
            console.error(err);
            res.send(err);
        });
};

function markProblematicIngredients(user,product){
    if(user && product){
        product.ingredient_analysis = [];
        if(user.allergies.length == 0){
            for(let ingredientIndex = 0; ingredientIndex <  product.ingredients.length; ingredientIndex++){
                product.ingredient_analysis.push({
                    name: product.ingredients[ingredientIndex],
                    analysis: 'UNKNOWN'
                });
            }
        }else {
            for (let ingredientIndex = 0; ingredientIndex < product.ingredients.length; ingredientIndex++) {
                let isSensetive = false;
                for (let allergyIndex = 0; allergyIndex < user.allergies.length; allergyIndex++) {
                    if (user.allergies[allergyIndex].originalObject.compound.toLowerCase().indexOf(product.ingredients[ingredientIndex].toLowerCase()) > -1) {
                        product.ingredient_analysis.push({
                            name: product.ingredients[ingredientIndex],
                            analysis: 'SENSITIVE'
                        });
                        isSensetive = true;
                        break;
                    }
                }
                if(!isSensetive) {
                    product.ingredient_analysis.push({
                        name: product.ingredients[ingredientIndex],
                        analysis: 'NOT_SENSITIVE'
                    })
                }
            }
        }

        product._doc.ingredient_analysis = product.ingredient_analysis;
    }
}

function saveUserSearch(user,product){
    user.searches.push(product);
    user.save();
}<|endoftext|>


<commit_before>/**
 * 优化 typeof 获取未知对象类型
 * @param  {mixed} mixed
 * @return {string}       Number|String|Object|Array|Function
 */
export const typeOf = mixed =>
  Object.prototype.toString.apply(mixed).match(/\[object (\w+)\]/)[1];

/**
 * 对象数据扩充
 * @param  {Object} obj 目标对象
 * @param  {object} ext 扩充对象
 * @return {object}
 */
export const assign = (obj = {}, ext = {}) => {
  for (let k in ext) {
    if (ext.hasOwnProperty(k)) {
      obj[k] = ext[k];
    }
  }
  return obj;
};

/**
 * object => serialize
 * @param  {Object} obj [description]
 * @return {[type]}     [description]
 */
export const serialize = (obj = {}) =>
  Object.keys(obj).map(k =>
    `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`
  ).join('&');

/**
 * hash search 转对象
 * ?a=a&b=b => {a:"a",b:"b"}
 * @param  {string} hash
 * @return {obj}
 */
export const search2obj = (hash = "") => {
  let ret = {},
    seg = decodeURIComponent(hash).replace(/^\?/, '').split('&'),
    len = seg.length,
    i = 0,
    s;
  for (; i < len; i++) {
    if (!seg[i]) {
      continue;
    }
    s = seg[i].split('=');
    ret[s[0]] = s[1];
  }
  return ret;
};

/**
 * xmlhttp 请求
 * @param  {string} url     请求地址
 * @param  {object} options 设置
 * @return {callback}
 */
export const xhr = (url, options = {}) => {
  let opts = assign({
      method: "GET",
      data: {},
      headers: {},
      cache: false,
      type: "json",
      done() {},
      fail() {},
      progress() {},
      complete() {}
    }, options),
    xhr, progress = 0,
    send_data = [],
    has_q = url.split("/").pop().search(/\?/) !== -1;
  // 是否有缓存
  if (!opts.cache) {
    url += (has_q ? "&" : "?") + "_=" + Math.random();
    has_q = true;
  }
  // 整理发送数据
  if(serialize(opts.data) !== ""){
    send_data.push(serialize(opts.data));
  }
  // 如果是put /post 则用formdata
  if (/^put$|^post$/i.test(opts.method)) {
    opts.headers["Content-type"] = "application/x-www-form-urlencoded";
  } else if(send_data.length > 0) {
    url += (has_q ? "&" : "?") + send_data;
  }
  xhr = new XMLHttpRequest();
  xhr.open(opts.method, url, true);
  for (let k in opts.headers) {
    xhr.setRequestHeader(k, opts.headers[k]);
  }
  // 如果支持进度条
  xhr.upload.onprogress = xhr.onprogress = (e) => {
    if (e.lengthComputable) {
      progress = Math.round(e.loaded * 100 / e.total);
      opts.progress.call(e.target, progress);
    }
  };
  xhr.addEventListener('load', (e) => {
    let res;
    if (e.target.status === 200 || e.target.status === 304) {
      res = e.target.responseText;
      if (opts.type === "json") {
        res = JSON.parse(res);
      }
      opts.done.call(e.target, res);
    }
    else{
      opts.fail.call(e.target, e.target.status);
    }
  }, false);
  xhr.addEventListener('error', () => {
    opts.fail();
  }, false);
  xhr.addEventListener('loadend', () => {
    opts.complete();
  }, false);
  // done().fail().progress()
  xhr.done = fn => {
    opts.done = fn;
    return xhr;
  }
  xhr.fail = fn => {
    opts.fail = fn;
    return xhr;
  }
  xhr.progress = fn => {
    opts.progress = fn;
    return xhr;
  }
  xhr.complete = fn => {
    opts.complete = fn;
    return xhr;
  }
  xhr.send(send_data);
  return xhr;
};

/**
 * 清除字符串中指定的标签
 * @param  {string} tag 标签名称
 * @param  {string} str 字符串
 * @return {string}
 */
export const tagRemove = (tag, str) => {
  return str.replace(new RegExp(`<${tag}(.|\\s)*?\\/${tag}>`, "g"), "");
};

/**
 * 解析字符串中的标签内容
 * @param  {string} tag 标签名称
 * @param  {string} 解析字符串
 * @return {string}
 */
export const tagContent = function(tag, str) {
  let re = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}>`, "gm"),
    text = "";
  for (let match = re.exec(str); match; match = re.exec(str)) {
    text += match[1];
  }
  return text;
};

export const cookie = {
  /**
   * 设置 cookie
   * @param  {string} name  项
   * @param  {String} value 值
   * @param  {Object} opts  扩展配置
   * @return {null}
   */
  set(name, value = "", options) {
    let data = {},
      cookies = [],
      opts = assign({
        path: "/",
        domain: ""
      }, options);
    if (!name ||
      /^(?:expires|max\-age|path|domain|secure)$/i.test(name)) {
      throw new Error("Cookie name is required and do not use cookie reserved keywords.");
    }
    data[encodeURIComponent(name)] = encodeURIComponent(value);
    if (opts["expires"]) {
      throw new Error("Please use max-age to replace expires");
    }
    for (let opt in opts) {
      data[opt] = opts[opt];
    }
    for (let key in data) {
      cookies.push(`${key}=${data[key]}`);
    }
    cookies = cookies.join("; ");
    document.cookie = `${cookies};`;
  },

  /**
   * 删除cookie某项
   * @param  {string} name
   * @return {null}
   */
  remove(name) {
    this.set(name, "", {
      "max-age": 0
    });
  },

  /**
   * 获取cookie某项
   * @param  {String} key [description]
   * @return {[type]}      [description]
   */
  get(key = "") {
    for (let _cookie of document.cookie.split("; ")) {
      let [name, value] = _cookie.split("=");
      if (key !== "" && key === name) {
        return decodeURIComponent(value);
      }
    }
  }

};

/**
 * 动态加载文件,建议私有化
 * @param  {String} type   加载远程文件类型 [script,link,img]
 * @param  {String} url    地址
 * @param  {Object} opts   附加配置
 * @return {Element}
 */
export const loadFile = (type = "script", url, options) => {
  let el = document.createElement(type),
    src = {
      script: "src",
      link: "href",
      img: "src"
    },
    opts = assign({
      position: "head",
      attrs: {},
      success() {},
      error() {}
    }, options);

  if (!src.hasOwnProperty(type)) {
    throw new Error(`File type:${type} is not support dynamic load.`);
  }
  if (typeof url === "undefined") {
    throw new Error("Load file url is required.");
  }
  // 扩展属性
  if (Object.keys(opts.attrs).length) {
    for (let _attr in opts.attrs) {
      el[_attr] = opts.attrs[_attr];
    }
  }
  el[src[type]] = url;
  if (type === "link") {
    el.rel = "stylesheet";
  }
  el.addEventListener("load", opts.success, false);
  el.addEventListener("error", opts.error, false);
  document[opts.position].appendChild(el);
  return el;
};

/**
 * 删除动态载入的文件标签，loadFile失败后可用，建议私有化
 * @param  {String} type [description]
 * @param  {String} rel  [description]
 * @return {null}      [description]
 */
export const removeFile = (type = "script", position = "head", rel) => {
  let i = 0,
    tags = document[position].getElementsByTagName(type);
  for (; i < tags.length; i++) {
    if (tags[i].rel === rel)
      tags[i].parentNode.removeChild(tags[i]);
  }
};

/**
 * 生成 hash
 * @param  {string} s
 * @return {hash}
 */
export const hashCode = s => {
  return s.split("").reduce((a, b) => {
    a = ((a << 5) - a) + b.charCodeAt(0);
    return a & a;
  }, 0);
};

/**
 * 在浏览器关闭之前缓存ajax获取的json数据
 * @param  {String} url           [description]
 * @param  {Object} options [description]
 * @return {Object}               [description]
 */
export const cacheJSON = (url, options={}) => {
  const name = `_hc_cache_json_${hashCode(url.replace(/^http[s]?:\/\//, ""))}`;
  let callback = assign({
    force: false, // true的时候强制ajax获取
    done(){}, fail(){}
  }, options);
  if ("localStorage" in window && cookie.get(`${name}`) && !callback.force) {
    setTimeout(() => {
      callback.done.call(null, JSON.parse(window.localStorage.getItem(name)));
    }, 0);
  }
  else{
    xhr(url).done(res => {
      // 关闭浏览器失效，保证下次浏览获取新的资源列表
      cookie.set(name, "y");
      window.localStorage.setItem(name, JSON.stringify(res));
      callback.done.call(null, res);
    })
    .fail(status => {
      callback.fail.call(null, status);
    });
  }
  // 支持 done().fail()
  callback.done = fn => {
    callback.done = fn;
    return callback;
  };
  callback.fail = fn => {
    callback.fail = fn;
    return callback;
  };
  return callback;
};
<commit_msg>no message<commit_after>/**
 * 优化 typeof 获取未知对象类型
 * @param  {mixed} mixed
 * @return {string}       Number|String|Object|Array|Function
 */
export const typeOf = mixed =>
  Object.prototype.toString.apply(mixed).match(/\[object (\w+)\]/)[1];

/**
 * 对象数据扩充
 * @param  {Object} obj 目标对象
 * @param  {object} ext 扩充对象
 * @return {object}
 */
export const assign = (obj = {}, ext = {}) => {
  for (let k in ext) {
    if (ext.hasOwnProperty(k)) {
      obj[k] = ext[k];
    }
  }
  return obj;
};

/**
 * object => serialize
 * @param  {Object} obj [description]
 * @return {[type]}     [description]
 */
export const serialize = (obj = {}) =>
  Object.keys(obj).map(k =>
    `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`
  ).join('&');

/**
 * hash search 转对象
 * ?a=a&b=b => {a:"a",b:"b"}
 * @param  {string} hash
 * @return {obj}
 */
export const search2obj = (hash = "") => {
  let ret = {},
    seg = decodeURIComponent(hash).replace(/^\?/, '').split('&'),
    len = seg.length,
    i = 0,
    s;
  for (; i < len; i++) {
    if (!seg[i]) {
      continue;
    }
    s = seg[i].split('=');
    ret[s[0]] = s[1];
  }
  return ret;
};

/**
 * xmlhttp 请求
 * @param  {string} url     请求地址
 * @param  {object} options 设置
 * @return {callback}
 */
export const xhr = (url, options = {}) => {
  let opts = assign({
      method: "GET",
      data: {},
      headers: {},
      cache: false,
      type: "json",
      done() {},
      fail() {},
      progress() {},
      complete() {}
    }, options),
    xhr, progress = 0,
    send_data = [],
    has_q = url.split("/").pop().search(/\?/) !== -1;
  // 是否有缓存
  if (!opts.cache) {
    url += (has_q ? "&" : "?") + "_=" + Math.random();
    has_q = true;
  }
  // 整理发送数据
  if(serialize(opts.data) !== ""){
    send_data.push(serialize(opts.data));
  }
  // 如果是put /post 则用formdata
  if (/^put$|^post$/i.test(opts.method)) {
    opts.headers["Content-type"] = "application/x-www-form-urlencoded";
  } else if(send_data.length > 0) {
    url += (has_q ? "&" : "?") + send_data;
  }
  xhr = new XMLHttpRequest();
  xhr.open(opts.method, url, true);
  for (let k in opts.headers) {
    xhr.setRequestHeader(k, opts.headers[k]);
  }
  // 如果支持进度条
  xhr.upload.onprogress = xhr.onprogress = (e) => {
    if (e.lengthComputable) {
      progress = Math.round(e.loaded * 100 / e.total);
      opts.progress.call(e.target, progress);
    }
  };
  xhr.addEventListener('load', (e) => {
    let res;
    if (e.target.status === 200 || e.target.status === 304) {
      res = e.target.responseText;
      if (opts.type === "json") {
        res = JSON.parse(res);
      }
      opts.done.call(e.target, res);
    }
    else{
      opts.fail.call(e.target, e.target.status);
    }
  }, false);
  xhr.addEventListener('error', () => {
    opts.fail();
  }, false);
  xhr.addEventListener('loadend', () => {
    opts.complete();
  }, false);
  // done().fail().progress()
  xhr.done = fn => {
    opts.done = fn;
    return xhr;
  }
  xhr.fail = fn => {
    opts.fail = fn;
    return xhr;
  }
  xhr.progress = fn => {
    opts.progress = fn;
    return xhr;
  }
  xhr.complete = fn => {
    opts.complete = fn;
    return xhr;
  }
  xhr.send(send_data);
  return xhr;
};

/**
 * 清除字符串中指定的标签
 * @param  {string} tag 标签名称
 * @param  {string} str 字符串
 * @return {string}
 */
export const tagRemove = (tag, str) => {
  return str.replace(new RegExp(`<${tag}(.|\\s)*?\\/${tag}>`, "g"), "");
};

/**
 * 解析字符串中的标签内容
 * @param  {string} tag 标签名称
 * @param  {string} 解析字符串
 * @return {string}
 */
export const tagContent = function(tag, str) {
  let re = new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)<\\/${tag}>`, "gm"),
    text = "";
  for (let match = re.exec(str); match; match = re.exec(str)) {
    text += match[1];
  }
  return text;
};

export const cookie = {
  /**
   * 设置 cookie
   * @param  {string} name  项
   * @param  {String} value 值
   * @param  {Object} opts  扩展配置
   * @return {null}
   */
  set(name, value = "", options) {
    let data = {},
      cookies = [],
      opts = assign({
        path: "/",
        domain: ""
      }, options);
    if (!name ||
      /^(?:expires|max\-age|path|domain|secure)$/i.test(name)) {
      throw new Error("Cookie name is required and do not use cookie reserved keywords.");
    }
    data[encodeURIComponent(name)] = encodeURIComponent(value);
    if (opts["expires"]) {
      throw new Error("Please use max-age to replace expires");
    }
    for (let opt in opts) {
      data[opt] = opts[opt];
    }
    for (let key in data) {
      cookies.push(`${key}=${data[key]}`);
    }
    cookies = cookies.join("; ");
    document.cookie = `${cookies};`;
  },

  /**
   * 删除cookie某项
   * @param  {string} name
   * @return {null}
   */
  remove(name) {
    this.set(name, "", {
      "max-age": 0
    });
  },

  /**
   * 获取cookie某项
   * @param  {String} key [description]
   * @return {[type]}      [description]
   */
  get(key = "") {
    for (let _cookie of document.cookie.split("; ")) {
      let [name, value] = _cookie.split("=");
      if (key !== "" && key === name) {
        return decodeURIComponent(value);
      }
    }
  }

};

/**
 * 动态加载文件,建议私有化
 * @param  {String} type   加载远程文件类型 [script,link,img]
 * @param  {String} url    地址
 * @param  {Object} opts   附加配置
 * @return {Element}
 */
export const loadFile = (type = "script", url, options) => {
  let el = document.createElement(type),
    src = {
      script: "src",
      link: "href",
      img: "src"
    },
    opts = assign({
      position: "head",
      attrs: {},
      success() {},
      error() {}
    }, options);

  if (!src.hasOwnProperty(type)) {
    throw new Error(`File type:${type} is not support dynamic load.`);
  }
  if (typeof url === "undefined") {
    throw new Error("Load file url is required.");
  }
  // 扩展属性
  if (Object.keys(opts.attrs).length) {
    for (let _attr in opts.attrs) {
      el[_attr] = opts.attrs[_attr];
    }
  }
  el[src[type]] = url;
  if (type === "link") {
    el.rel = "stylesheet";
  }
  el.addEventListener("load", opts.success, false);
  el.addEventListener("error", opts.error, false);
  document[opts.position].appendChild(el);
  return el;
};

/**
 * 删除动态载入的文件标签，loadFile失败后可用，建议私有化
 * @param  {String} type [description]
 * @param  {String} rel  [description]
 * @return {null}      [description]
 */
export const removeFile = (type = "script", position = "head", rel) => {
  let i = 0,
    tags = document[position].getElementsByTagName(type);
  for (; i < tags.length; i++) {
    if (tags[i].rel === rel)
      tags[i].parentNode.removeChild(tags[i]);
  }
};

/**
 * 生成 hash
 * @param  {string} s
 * @return {hash}
 */
export const hashCode = s => {
  return s.split("").reduce((a, b) => {
    a = ((a << 5) - a) + b.charCodeAt(0);
    return a & a;
  }, 0);
};

/**
 * 在浏览器关闭之前缓存ajax获取的json数据
 * @param  {String} url           [description]
 * @param  {Object} options [description]
 * @return {Object}               [description]
 */
export const cacheJSON = (url, options={}) => {
  const name = `_hc_json_${url.split("/").pop().split(".")[0]}`;
  let callback = assign({
    force: false, // true的时候强制ajax获取
    done(){}, fail(){}
  }, options);
  if ("localStorage" in window && cookie.get(`${name}`) && !callback.force) {
    setTimeout(() => {
      callback.done.call(null, JSON.parse(window.localStorage.getItem(name)));
    }, 0);
  }
  else{
    xhr(url).done(res => {
      // 关闭浏览器失效，保证下次浏览获取新的资源列表
      cookie.set(name, "y");
      window.localStorage &&
      window.localStorage.setItem(name, JSON.stringify(res));
      callback.done.call(null, res);
    })
    .fail(status => {
      callback.fail.call(null, status);
    });
  }
  // 支持 done().fail()
  callback.done = fn => {
    callback.done = fn;
    return callback;
  };
  callback.fail = fn => {
    callback.fail = fn;
    return callback;
  };
  return callback;
};
<|endoftext|>


<commit_before>// -*- Mode: JavaScript; tab-width: 4 -*- vim:tabstop=4 syntax=javascript:
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*jshint es5: true, esnext: true, expr: true */
/*global Stream: false, aucgbot: false, module: false, system: false */

module.version = 2.1;

module.cmd_ytid =
function cmd_ytid(dest, msg, nick, ident, host, conn, relay) {
	if (!/^(?:(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?v=|(?:v|embed)\/)|youtu\.be\/))?([\w\-]+)(?:[?&#].*)?$/i.test(msg)) {
		conn.reply(dest, nick, "Get info about a YouTube video. Usage: yt <link|id>");
		return true;
	}
	var id = RegExp.$1, data, stream = new Stream("http://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=jsonc", null,
		{"User-Agent": aucgbot.useragent + " mod_yt/" + this.version});
	try {
		let s = decodeUTF8(stream.readFile());
		data = JSON.parse(s).data;
	} catch (ex) {}
	stream.close();
	if (!data) {
		conn.reply(dest, nick, "YouTube returned no data.");
		return true;
	}
	this.ytRes(data, dest, msg, nick, conn);
	return true;
};
module.cmd_yt = module.cmd_youtube =
function cmd_yt(dest, msg, nick, ident, host, conn, relay) {
	if (!msg) {
		conn.reply(dest, nick, "Get the first result of a YouTube search.");
		return true;
	}
	var data, stream = new Stream("http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=1&q=" + encodeURIComponent(msg), null,
		{"User-Agent": aucgbot.useragent + " mod_yt/" + this.version});
	try {
		let s = decodeUTF8(stream.readFile());
		data = JSON.parse(s).data;
	} catch (ex) {}
	stream.close();
	if (!data) {
		conn.reply(dest, nick, "YouTube returned no data.");
		return true;
	}
	if (!data.totalItems) {
		conn.reply(dest, nick, "No results.");
		return true;
	}
	this.ytRes(data.items[0], dest, msg, nick, conn);
	return true;
};

module.ytRes = function ytRes(data, dest, msg, nick, conn) {
	var res = [data.title, data.uploader];

	{
		let dura = data.duration,
			f = function(n) n < 10 ? "0" + n : n,
			m = f(Math.floor((dura % 3600) / 60)),
			s = f(dura % 60),
			h = Math.floor(dura / 3600);
		res.push((h ? h + ":" : "") + m + ":" + s);
	}

	if (data.description)
		res.push(data.description);

	if (data.rating)
		res.push(data.rating.toFixed(2) + "/5 (" + data.likeCount + "+ " + (data.ratingCount - data.likeCount) + "-)");

	res.push(data.viewCount + " views");

	if (data.commentCount)
		res.push(data.commentCount + " comments");

	res.push(data.category);

	if (data.id == msg)
		res.push("https://youtu.be/" + data.id);

	conn.reply(dest, nick, res.join(" - "));
};
<commit_msg>[yt] oi, where's my URL?<commit_after>// -*- Mode: JavaScript; tab-width: 4 -*- vim:tabstop=4 syntax=javascript:
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*jshint es5: true, esnext: true, expr: true */
/*global Stream: false, aucgbot: false, module: false, system: false */

module.version = 2.1;

module.cmd_ytid =
function cmd_ytid(dest, msg, nick, ident, host, conn, relay) {
	if (!/^(?:(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?v=|(?:v|embed)\/)|youtu\.be\/))?([\w\-]+)(?:[?&#].*)?$/i.test(msg)) {
		conn.reply(dest, nick, "Get info about a YouTube video. Usage: yt <link|id>");
		return true;
	}

	var id = RegExp.$1, data, stream = new Stream("http://gdata.youtube.com/feeds/api/videos/" + id + "?v=2&alt=jsonc", null,
		{"User-Agent": aucgbot.useragent + " mod_yt/" + this.version});
	try {
		let s = decodeUTF8(stream.readFile());
		data = JSON.parse(s).data;
	} catch (ex) {}
	stream.close();

	if (!data) {
		conn.reply(dest, nick, "YouTube returned no data.");
		return true;
	}

	var res = this.ytRes(data);
	if (id == msg)
		res.push("https://youtu.be/" + id);

	conn.reply(dest, nick, res.join(" - "));
	return true;
};

module.cmd_yt = module.cmd_youtube =
function cmd_yt(dest, msg, nick, ident, host, conn, relay) {
	if (!msg) {
		conn.reply(dest, nick, "Get the first result of a YouTube search.");
		return true;
	}

	var data, stream = new Stream("http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=1&q=" + encodeURIComponent(msg), null,
		{"User-Agent": aucgbot.useragent + " mod_yt/" + this.version});
	try {
		let s = decodeUTF8(stream.readFile());
		data = JSON.parse(s).data;
	} catch (ex) {}
	stream.close();

	if (!data) {
		conn.reply(dest, nick, "YouTube returned no data.");
		return true;
	}

	if (!data.totalItems) {
		conn.reply(dest, nick, "No results.");
		return true;
	}

	var item = data.items[0], res = this.ytRes(item);
	res.push("https://youtu.be/" + item.id);

	conn.reply(dest, nick, res.join(" - "));
	return true;
};

module.ytRes = function ytRes(data) {
	var res = [data.title, data.uploader];

	{
		let dura = data.duration,
			f = function(n) n < 10 ? "0" + n : n,
			m = f(Math.floor((dura % 3600) / 60)),
			s = f(dura % 60),
			h = Math.floor(dura / 3600);
		res.push((h ? h + ":" : "") + m + ":" + s);
	}

	if (data.description)
		res.push(data.description);

	if (data.rating)
		res.push(data.rating.toFixed(2) + "/5 (" + data.likeCount + "+ " + (data.ratingCount - data.likeCount) + "-)");

	res.push(data.viewCount + " views");

	if (data.commentCount)
		res.push(data.commentCount + " comments");

	res.push(data.category);

	return res;
};
<|endoftext|>