<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

var OpenSimToolbar = function ( editor ) {

	var signals = editor.signals;

	var container = new UI.Panel();
	container.setId( 'toolbar' );

	var buttons = new UI.Panel();
	container.add( buttons );

	var camera = editor.camera;
    // -X
	var viewminx = new UI.Button('-X').onClick(function () {
		viewfromMinusX();
	});
	buttons.add(viewminx);
	function viewfromMinusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.min.x - distance;
	    updateCamera(newpos, center);
	};
    // +X
	var viewx = new UI.Button('+X').onClick(function () {

	    viewfromPlusX();

	});
	buttons.add(viewx);

	function viewfromPlusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    // If you want a visible bounding box
	    //editor.scene.add(helper);
	    // create a sphere at new CameraPos
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.max.x + distance;
	    updateCamera(newpos, center);
	};
    // -Y
	var viewminy = new UI.Button('-Y').onClick(function () {
	    viewfromMinusY();
	});
	buttons.add(viewminy);
	function viewfromMinusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.min.y - distance;
	    updateCamera(newpos, center);
	};
    // +Y
	var viewplusy = new UI.Button('+Y').onClick(function () {
	    viewfromPlusY();
	});
	buttons.add(viewplusy);
	function viewfromPlusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.max.y + distance;
	    updateCamera(newpos, center);
	};
    // -Z
	var viewminz = new UI.Button('-Z').onClick(function () {
	    viewfromMinusZ();
	});
	buttons.add(viewminz);
	function viewfromMinusZ() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.min.z - distance;
	    updateCamera(newpos, center);
	};
    // +Z
	var viewplusz = new UI.Button('+Z').onClick(function () {
	    viewfromPlusZ();
	});
	buttons.add(viewplusz);
	function viewfromPlusZ() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.max.z + distance;
	    updateCamera(newpos, center);
	};
	var snapshot = new UI.Button('Snap').onClick(function () {
            saveAsImage();
	});
	buttons.add(snapshot);

	function updateCamera(newposition, viewCenter) {
	    editor.camera.position.copy(newposition);
	    editor.camera.lookAt(viewCenter.x, viewCenter.y, viewCenter.z);
	    editor.camera.updateProjectionMatrix();

	};
	function computeModelBbox() {
	    var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    return (new THREE.Box3().setFromObject(modelObject));
	};
        function saveAsImage() {
            var canvas = document.getElementById("viewport");
            getImageData = true;
            //canvas.children[1].render();
	    var img    = canvas.children[1].toDataURL("image/jpeg");
	    saveFile(img, "opensim_snapshot.jpg");
        }
        // Support saving image to file
        var saveFile = function (strData, filename) {
        var link = document.createElement('a');
        if (typeof link.download === 'string') {
            document.body.appendChild(link); //Firefox requires the link to be in the body
            link.download = filename;
            link.href = strData;
            link.click();
            document.body.removeChild(link); //remove the link when done
        } else {
            location.replace(uri);
        }
    }
	return container;

}
<commit_msg>Add zoom-in/out buttons to experiment (eventually shortcuts)<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

var OpenSimToolbar = function ( editor ) {

	var signals = editor.signals;

	var container = new UI.Panel();
	container.setId( 'toolbar' );

	var buttons = new UI.Panel();
	container.add( buttons );

	var camera = editor.camera;
    // -X
	var viewminx = new UI.Button('-X').onClick(function () {
		viewfromMinusX();
	});
	buttons.add(viewminx);
	function viewfromMinusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.min.x - distance;
	    updateCamera(newpos, center);
	};
    // +X
	var viewx = new UI.Button('+X').onClick(function () {

	    viewfromPlusX();

	});
	buttons.add(viewx);

	function viewfromPlusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    // If you want a visible bounding box
	    //editor.scene.add(helper);
	    // create a sphere at new CameraPos
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.max.x + distance;
	    updateCamera(newpos, center);
	};
    // -Y
	var viewminy = new UI.Button('-Y').onClick(function () {
	    viewfromMinusY();
	});
	buttons.add(viewminy);
	function viewfromMinusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.min.y - distance;
	    updateCamera(newpos, center);
	};
    // +Y
	var viewplusy = new UI.Button('+Y').onClick(function () {
	    viewfromPlusY();
	});
	buttons.add(viewplusy);
	function viewfromPlusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.max.y + distance;
	    updateCamera(newpos, center);
	};
    // -Z
	var viewminz = new UI.Button('-Z').onClick(function () {
	    viewfromMinusZ();
	});
	buttons.add(viewminz);
	function viewfromMinusZ() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.min.z - distance;
	    updateCamera(newpos, center);
	};
    // +Z
	var viewplusz = new UI.Button('+Z').onClick(function () {
	    viewfromPlusZ();
	});
	buttons.add(viewplusz);
	function viewfromPlusZ() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.max.z + distance;
	    updateCamera(newpos, center);
	};

	var view_zoomin = new UI.Button('+').onClick(function () {
	    viewZoom(+1);
	});
	buttons.add(view_zoomin);
	var view_zoomout = new UI.Button('-').onClick(function () {
	    viewZoom(-1);
	});
	buttons.add(view_zoomout);

	function viewZoom(in_out) {
	    var vector = new THREE.Vector3(0, 0, -100 * in_out);
	    vector.applyQuaternion(camera.quaternion);
	    var newPos = camera.position.add(vector);
	    camera.position.copy(newPos);
	    signals.cameraChanged.dispatch(camera);
	};

	var snapshot = new UI.Button('Snap').onClick(function () {
            saveAsImage();
	});
	buttons.add(snapshot);

	function updateCamera(newposition, viewCenter) {
	    camera.position.copy(newposition);
	    camera.lookAt(viewCenter.x, viewCenter.y, viewCenter.z);
	    camera.updateProjectionMatrix();
        //transformControls.update();
	    signals.cameraChanged.dispatch( this.camera );

	};
	function computeModelBbox() {
	    var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    return (new THREE.Box3().setFromObject(modelObject));
	};
        function saveAsImage() {
            var canvas = document.getElementById("viewport");
            getImageData = true;
            //canvas.children[1].render();
	    var img    = canvas.children[1].toDataURL("image/jpeg");
	    saveFile(img, "opensim_snapshot.jpg");
        }
        // Support saving image to file
        var saveFile = function (strData, filename) {
        var link = document.createElement('a');
        if (typeof link.download === 'string') {
            document.body.appendChild(link); //Firefox requires the link to be in the body
            link.download = filename;
            link.href = strData;
            link.click();
            document.body.removeChild(link); //remove the link when done
        } else {
            location.replace(uri);
        }
    }
	return container;

}
<|endoftext|>


<commit_before>var env = process.env.NODE_ENV || 'development';
var headers = { 'Content-Type': 'application/json' };

server = Bones.Server.extend({});

var options = {
    type: '.js',
    wrapper: Bones.utils.wrapClientFile,
    sort: Bones.utils.sortByLoadOrder
};

// TODO: This should be moved to the initialize method!
server.prototype.assets = {
    vendor: new mirror([
        require.resolve('bones/assets/jquery'),
        require.resolve('underscore'),
        require.resolve('backbone')
    ], { type: '.js' }),
    core: new mirror([
        require.resolve('bones/shared/utils'),
        require.resolve('bones/client/utils'),
        require.resolve('bones/shared/backbone'),
        require.resolve('bones/client/backbone')
    ], { type: '.js' }),
    models: new mirror([], options),
    views: new mirror([], options),
    routers: new mirror([], options),
    templates: new mirror([], options)
};

if (env === 'development') {
    server.prototype.assets.core.unshift(require.resolve('bones/assets/debug'));
}

// TODO: This should be moved to the initialize method!
server.prototype.assets.all = new mirror([
    server.prototype.assets.vendor,
    server.prototype.assets.core,
    server.prototype.assets.routers,
    server.prototype.assets.models,
    server.prototype.assets.views,
    server.prototype.assets.templates
], { type: '.js' });

// Stores models, views served by this server.
// TODO: This should be moved to the initialize method!
server.prototype.models = {};
server.prototype.views = {};

// Stores instances of routers registered with this server.
// TODO: This should be moved to the initialize method!
server.prototype.routers = {};

server.prototype.initialize = function(app) {
    this.registerComponents(app);
    this.initializeAssets(app);
    this.initializeModels(app);
};

server.prototype.registerComponents = function(app) {
    var components = ['routers', 'models', 'views', 'templates'];
    components.forEach(function(kind) {
        for (var name in app[kind]) {
            app[kind][name].register(this);
        }
    }, this);
};

server.prototype.initializeAssets = function(app) {
    this.get('/assets/bones/vendor.js', this.assets.vendor.handler);
    this.get('/assets/bones/core.js', this.assets.core.handler);
    this.get('/assets/bones/routers.js', this.assets.routers.handler);
    this.get('/assets/bones/models.js', this.assets.models.handler);
    this.get('/assets/bones/views.js', this.assets.views.handler);
    this.get('/assets/bones/templates.js', this.assets.templates.handler);

    this.get('/assets/bones/all.js', this.assets.all.handler);
};

server.prototype.initializeModels = function(app) {
    this.models = app.models;
    _.bindAll(this, 'loadModel', 'accessModel','getModel', 'fillModel', 'saveModel', 'delModel', 'loadCollection', 'accessCollection', 'getCollection');
    this.get('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.getModel);
    this.post('/api/:model', this.loadModel, this.accessModel, this.saveModel);
    this.put('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.saveModel);
    this.del('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.delModel);
    this.get('/api/:collection', this.loadCollection, this.accessCollection, this.getCollection);
};

server.prototype.loadCollection = function(req, res, next) {
    var name = Bones.utils.pluralize(req.params.collection);
    if (name in this.models) {
        // Pass any querystring parameters to the collection.
        req.collection = new this.models[name]([], req.query);
    }
    return next();
};

server.prototype.getCollection = function(req, res, next) {
    if (!req.collection) { return next(); }
    req.collection.fetch({
        success: function(collection, resp) {
            res.send(resp, headers);
        },
        error: function(collection, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 500));
        }
    });
};

server.prototype.loadModel = function(req, res, next) {
    var name = req.params.model;
    if (name in this.models) {
        // Pass any querystring parameters to the model.
        req.model = new this.models[name]({ id: req.params.id }, req.query);
    }
    next();
};

/*
 * Check the query should be allowed access to the given model.
 */
server.prototype.accessModel = function(req, res, next) {
    if (!req.model) return next();
    if (req.model.access(req, res)) {
        return next();
    } else {
        return next(new Error.HTTP(403));
    }
};

server.prototype.accessCollection = function(req, res, next) {
    if (!req.collection) { return next(); }
    if (req.collection.access(req, res)) {
        return next();
    } else {
        return next(new Error.HTTP(403));
    }
};

server.prototype.getModel = function(req, res, next) {
    if (!req.model) return next();
    res.send(resp, headers);
};

server.prototype.fillModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.fetch({
        success: function(model, resp) {
            next();
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 404));
        }
    });
};

server.prototype.saveModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.save(req.body, {
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 409));
        }
    });
};

server.prototype.delModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.destroy({
        success: function(model, resp) {
            res.send({}, headers);
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 409));
        }
    });
};
<commit_msg>getModel properly sends req.model now.<commit_after>var env = process.env.NODE_ENV || 'development';
var headers = { 'Content-Type': 'application/json' };

server = Bones.Server.extend({});

var options = {
    type: '.js',
    wrapper: Bones.utils.wrapClientFile,
    sort: Bones.utils.sortByLoadOrder
};

// TODO: This should be moved to the initialize method!
server.prototype.assets = {
    vendor: new mirror([
        require.resolve('bones/assets/jquery'),
        require.resolve('underscore'),
        require.resolve('backbone')
    ], { type: '.js' }),
    core: new mirror([
        require.resolve('bones/shared/utils'),
        require.resolve('bones/client/utils'),
        require.resolve('bones/shared/backbone'),
        require.resolve('bones/client/backbone')
    ], { type: '.js' }),
    models: new mirror([], options),
    views: new mirror([], options),
    routers: new mirror([], options),
    templates: new mirror([], options)
};

if (env === 'development') {
    server.prototype.assets.core.unshift(require.resolve('bones/assets/debug'));
}

// TODO: This should be moved to the initialize method!
server.prototype.assets.all = new mirror([
    server.prototype.assets.vendor,
    server.prototype.assets.core,
    server.prototype.assets.routers,
    server.prototype.assets.models,
    server.prototype.assets.views,
    server.prototype.assets.templates
], { type: '.js' });

// Stores models, views served by this server.
// TODO: This should be moved to the initialize method!
server.prototype.models = {};
server.prototype.views = {};

// Stores instances of routers registered with this server.
// TODO: This should be moved to the initialize method!
server.prototype.routers = {};

server.prototype.initialize = function(app) {
    this.registerComponents(app);
    this.initializeAssets(app);
    this.initializeModels(app);
};

server.prototype.registerComponents = function(app) {
    var components = ['routers', 'models', 'views', 'templates'];
    components.forEach(function(kind) {
        for (var name in app[kind]) {
            app[kind][name].register(this);
        }
    }, this);
};

server.prototype.initializeAssets = function(app) {
    this.get('/assets/bones/vendor.js', this.assets.vendor.handler);
    this.get('/assets/bones/core.js', this.assets.core.handler);
    this.get('/assets/bones/routers.js', this.assets.routers.handler);
    this.get('/assets/bones/models.js', this.assets.models.handler);
    this.get('/assets/bones/views.js', this.assets.views.handler);
    this.get('/assets/bones/templates.js', this.assets.templates.handler);

    this.get('/assets/bones/all.js', this.assets.all.handler);
};

server.prototype.initializeModels = function(app) {
    this.models = app.models;
    _.bindAll(this, 'loadModel', 'accessModel','getModel', 'fillModel', 'saveModel', 'delModel', 'loadCollection', 'accessCollection', 'getCollection');
    this.get('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.getModel);
    this.post('/api/:model', this.loadModel, this.accessModel, this.saveModel);
    this.put('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.saveModel);
    this.del('/api/:model/:id', this.loadModel, this.fillModel, this.accessModel, this.delModel);
    this.get('/api/:collection', this.loadCollection, this.accessCollection, this.getCollection);
};

server.prototype.loadCollection = function(req, res, next) {
    var name = Bones.utils.pluralize(req.params.collection);
    if (name in this.models) {
        // Pass any querystring parameters to the collection.
        req.collection = new this.models[name]([], req.query);
    }
    return next();
};

server.prototype.getCollection = function(req, res, next) {
    if (!req.collection) { return next(); }
    req.collection.fetch({
        success: function(collection, resp) {
            res.send(resp, headers);
        },
        error: function(collection, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 500));
        }
    });
};

server.prototype.loadModel = function(req, res, next) {
    var name = req.params.model;
    if (name in this.models) {
        // Pass any querystring parameters to the model.
        req.model = new this.models[name]({ id: req.params.id }, req.query);
    }
    next();
};

/*
 * Check the query should be allowed access to the given model.
 */
server.prototype.accessModel = function(req, res, next) {
    if (!req.model) return next();
    console.log('[debug accessModel] now trying to access: ', req.model);
    if (req.model.access(req, res)) {
        return next();
    } else {
        return next(new Error.HTTP(403));
    }
};

server.prototype.accessCollection = function(req, res, next) {
    if (!req.collection) { return next(); }
    if (req.collection.access(req, res)) {
        return next();
    } else {
        return next(new Error.HTTP(403));
    }
};

server.prototype.getModel = function(req, res, next) {
    if (!req.model) return next();
    res.send(req.model, headers);
};

server.prototype.fillModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.fetch({
        success: function(model, resp) {
            next();
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 404));
        }
    });
};

server.prototype.saveModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.save(req.body, {
        success: function(model, resp) {
            res.send(resp, headers);
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 409));
        }
    });
};

server.prototype.delModel = function(req, res, next) {
    if (!req.model) return next();
    req.model.destroy({
        success: function(model, resp) {
            res.send({}, headers);
        },
        error: function(model, err) {
            err = err instanceof Object ? err.toString() : err;
            next(new Error.HTTP(err, 409));
        }
    });
};
<|endoftext|>


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

// CLASS({
//   name: 'InheritedProperty',
//   extendsModel: 'Property',
//   package: 'foam.views',
//   traits: ['foam.patterns.InheritedPropertyTrait']
// });

// CLASS({
//   name: 'ContextMutableProperty',
//   extendsModel: 'Property',
//   package: 'foam.views',
//   traits: ['foam.patterns.ContextMutablePropertyTrait']
// });

//
// CLASS({
//   name: 'AbstractDAOView',
//   package: 'foam.ui',
//
//   extendsModel: 'AbstractDAOView',
//   traits: ['foam.ui.DataConsumerTrait'],
//
// });

CLASS({
  name: 'CollapsibleView',
  package: 'foam.ui',
  extendsModel: 'foam.ui.View',

  properties: [
    {
      name:  'fullView',
      documentation: function() {/*
        The large, expanded view to show.
      */}
    },
    {
      name:  'collapsedView',
      documentation: function() {/*
        The small, hidden mode view to show.
      */}

    },
    {
      name: 'collapsed',
      documentation: function() {/*
        Indicates if the collapsed or full view is shown.
      */},
      defaultValue: true,
      postSet: function() {
        if (this.collapsed) {
          this.collapsedView.$.style.height = "";
          this.fullView.$.style.height = "0";

        } else {
          this.collapsedView.$.style.height = "0";
          this.fullView.$.style.height = "";
        }
      }
    }

  ],

  methods: {
    toHTML: function() {
      /* Just render both sub-views, and control their height to show or hide. */

      // TODO: don't render full view until expanded for the first time?
      if (this.collapsedView && this.fullView) {
        var retStr = this.collapsedView.toHTML() + this.fullView.toHTML();
        this.addChild(this.collapsedView);
        this.addChild(this.fullView);
      } else {
        console.warn(model_.id + " missing "
            + ( this.collapsedView ? "" : "collapsedView" )
            + ( this.fullView ? "" : "fulleView" ));
      }
      return retStr;
    },

    initHTML: function() {
      this.SUPER();
      /* Just render both sub-views, and control their height to show or hide. */

      if (this.collapsedView.$ && this.fullView.$) {
        // to ensure we can hide by setting the height
        this.collapsedView.$.style.display = "block";
        this.fullView.$.style.display = "block";
        this.collapsedView.$.style.overflow = "hidden";
        this.fullView.$.style.overflow = "hidden";
        this.collapsed = true;
      }
    }
  },

  actions: [
    {
      name:  'toggle',
      help:  'Toggle collapsed state.',

      labelFn: function() {
        return this.collapsed? 'Expand' : 'Hide';
      },
      isAvailable: function() {
        return true;
      },
      isEnabled: function() {
        return true;//this.collapsedView.toHTML && this.fullView.toHTML;
      },
      action: function() {
        this.collapsed = !this.collapsed;
      }
    },
  ]
});

<commit_msg>removed old dataviews.js file<commit_after><|endoftext|>


<commit_before>    var customIcons = {
        building: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/building_icon.png'
        },
        student_centre: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/student_centre_icon.png'
        },
        lecture_theatre: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/lecture_theatre_icon.png'
        },
        computer_lab: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/computer_lab_icon.png'
        },
        library: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/library_icon.png'
        },
        cycle_hire: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/cycle_hire_icon.png'
        },
        cycle_parking: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/cycle_parking_icon.png'
        },
        atm: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/atm_icon.png'
        }
    };

    var markerGroups = {
        "building": [],
        "student_centre": [],
        "lecture_theatre": [],
        "computer_lab": [],
        "library": [],
        "cycle_hire": [],
        "cycle_parking": [],
        "atm": []
    };

    function showCurrentLocation(currentLocationDiv, map) {

        currentLocationDiv.style.marginTop = '5px';
        var currentLocationUI = document.createElement('div');
        currentLocationUI.style.backgroundColor = '#4582EC';
        currentLocationUI.style.borderRadius = '3px';
        currentLocationUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
        currentLocationUI.style.cursor = 'pointer';
        currentLocationUI.style.marginBottom = '22px';
        currentLocationUI.style.textAlign = 'center';
        currentLocationUI.title = 'Click to show current location';
        currentLocationDiv.appendChild(currentLocationUI);

        var currentLocationText = document.createElement('div');
        currentLocationText.style.color = '#FFFFFF';
        currentLocationText.style.fontFamily = 'Open Sans,Arial,sans-serif';
        currentLocationText.style.fontSize = '15px';
        currentLocationText.style.lineHeight = '30px';
        currentLocationText.style.paddingLeft = '5px';
        currentLocationText.style.paddingRight = '5px';
        currentLocationText.innerHTML = 'Current location';
        currentLocationUI.appendChild(currentLocationText);

        var marker = null;

        google.maps.event.addDomListener(currentLocationUI, 'click', function () {

            if(navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                    var pos = new google.maps.LatLng(position.coords.latitude,
                        position.coords.longitude);

                    if (marker == null) {
                        marker = new google.maps.Marker({
                            map: map,
                            position: pos,
                            title: 'You are here.'
                        });

                        var infowindow = new google.maps.InfoWindow({
                            content: 'You are here.'
                        });

                        infowindow.open(map,marker);

                        google.maps.event.addListener(marker, 'click', function() {
                            infowindow.open(map,marker);
                        });

                        map.setCenter(pos);
                    } else {
                        map.setCenter(pos);
                        marker.setPosition(pos);
                    }

                }, function() {
                    handleNoGeolocation(true);
                });
            } else {
                handleNoGeolocation(false);
            }

        function handleNoGeolocation(errorFlag) {
            if (errorFlag) {
                var content = 'Error: The Geolocation service failed.';
            } else {
                var content = 'Error: Your browser doesn\'t support geolocation.';
            }

            var options = {
                map: map,
                position: new google.maps.LatLng(51.527287, -0.103842),
                content: content
            };

            if (marker == null) {
                marker = new google.maps.Marker({
                    map: map,
                    position: options.position,
                    title: content
                });

                var infowindow = new google.maps.InfoWindow({
                    content: content
                });

                infowindow.open(map,marker);

                google.maps.event.addListener(marker, 'click', function() {
                    infowindow.open(map,marker);
                });

                map.setCenter(options.position);
            } else {
                map.setCenter(options.position);
                marker.setPosition(options.position);
            }
        }

        });
    }

    function loadMap() {
    var map = new google.maps.Map(document.getElementById("map"), {
        center: new google.maps.LatLng(51.527287, -0.103842),
        zoom: 15,
        mapTypeId: 'roadmap'
    });

        // Create a DIV to hold the control and call HomeControl()
    var currentLocationDiv = document.createElement('div');
    var currentLocation = new showCurrentLocation(currentLocationDiv, map);
    map.controls[google.maps.ControlPosition.TOP_CENTER].push(currentLocationDiv);

    downloadUrl("../../includes/university-map/source/overview_source.php", function(data) {
    var xml = data.responseXML;
    var markers = xml.documentElement.getElementsByTagName("marker");
    for (var i = 0; i < markers.length; i++) {
        var name = markers[i].getAttribute("name");
        var notes = markers[i].getAttribute("notes");
        var category = markers[i].getAttribute("category");
        var point = new google.maps.LatLng(
            parseFloat(markers[i].getAttribute("lat")),
            parseFloat(markers[i].getAttribute("lng")));
        var marker = createMarker(point, name, notes, category, map);
    }
    });
    }

    var infoWindow = new google.maps.InfoWindow({
        maxWidth: 400
    });

    function createMarker(point, name, notes, category, map) {
        var icon = customIcons[category] || {};
        var marker = new google.maps.Marker({
            map: map,
            position: point,
            icon: icon.icon,
            type: category
        });
        if (!markerGroups[category]) markerGroups[category] = [];
        markerGroups[category].push(marker);
        var html = "" + name + "
" + notes;
        bindInfoWindow(marker, map, infoWindow, html);
        return marker;
    }

    function toggleGroup(category) {
        for (var i = 0; i < markerGroups[category].length; i++) {
            var marker = markerGroups[category][i];
            if (!marker.getVisible()) {
                marker.setVisible(true);
            } else {
                marker.setVisible(false);
            }
        }
    }

    function bindInfoWindow(marker, map, infoWindow, html) {
        google.maps.event.addListener(marker, 'click', function() {
            infoWindow.setContent(html);
            infoWindow.open(map, marker);

        });
    }

    function downloadUrl(url, callback) {
        var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();

        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                request.onreadystatechange = doNothing;
                callback(request, request.status);
            }
        };

        request.open('GET', url, true);
        request.send(null);
    }

    function doNothing() {}
<commit_msg>Changed structure<commit_after>    var customIcons = {
        building: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/building_icon.png'
        },
        student_centre: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/student_centre_icon.png'
        },
        lecture_theatre: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/lecture_theatre_icon.png'
        },
        computer_lab: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/computer_lab_icon.png'
        },
        library: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/library_icon.png'
        },
        cycle_hire: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/cycle_hire_icon.png'
        },
        cycle_parking: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/cycle_parking_icon.png'
        },
        atm: {
            icon: 'https://student-portal.co.uk/assets/img/university-map/atm_icon.png'
        }
    };

    var markerGroups = {
        "building": [],
        "student_centre": [],
        "lecture_theatre": [],
        "computer_lab": [],
        "library": [],
        "cycle_hire": [],
        "cycle_parking": [],
        "atm": []
    };

    function showCurrentLocation(currentLocationDiv, map) {

        currentLocationDiv.style.marginTop = '5px';
        var currentLocationUI = document.createElement('div');
        currentLocationUI.style.backgroundColor = '#4582EC';
        currentLocationUI.style.borderRadius = '3px';
        currentLocationUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
        currentLocationUI.style.cursor = 'pointer';
        currentLocationUI.style.marginBottom = '22px';
        currentLocationUI.style.textAlign = 'center';
        currentLocationUI.title = 'Click to show current location';
        currentLocationDiv.appendChild(currentLocationUI);

        var currentLocationText = document.createElement('div');
        currentLocationText.style.color = '#FFFFFF';
        currentLocationText.style.fontFamily = 'Open Sans,Arial,sans-serif';
        currentLocationText.style.fontSize = '15px';
        currentLocationText.style.lineHeight = '30px';
        currentLocationText.style.paddingLeft = '5px';
        currentLocationText.style.paddingRight = '5px';
        currentLocationText.innerHTML = 'Current location';
        currentLocationUI.appendChild(currentLocationText);

        var marker = null;

        google.maps.event.addDomListener(currentLocationUI, 'click', function () {

            if(navigator.geolocation) {
                navigator.geolocation.getCurrentPosition(function(position) {
                    var pos = new google.maps.LatLng(position.coords.latitude,
                        position.coords.longitude);

                    if (marker == null) {
                        marker = new google.maps.Marker({
                            map: map,
                            position: pos,
                            title: 'You are here.'
                        });

                        var infowindow = new google.maps.InfoWindow({
                            content: 'You are here.'
                        });

                        google.maps.event.addListener(marker, 'click', function() {
                            infowindow.open(map,marker);
                        });

                        map.setCenter(pos);
                    } else {
                        map.setCenter(pos);
                        marker.setPosition(pos);
                    }

                }, function() {
                    handleNoGeolocation(true);
                });
            } else {
                handleNoGeolocation(false);
            }

        function handleNoGeolocation(errorFlag) {
            if (errorFlag) {
                var content = 'Error: The Geolocation service failed.';
            } else {
                var content = 'Error: Your browser doesn\'t support geolocation.';
            }

            var options = {
                map: map,
                position: new google.maps.LatLng(51.527287, -0.103842),
                content: content
            };

            if (marker == null) {
                marker = new google.maps.Marker({
                    map: map,
                    position: options.position,
                    title: content
                });

                var infowindow = new google.maps.InfoWindow({
                    content: content
                });

                google.maps.event.addListener(marker, 'click', function() {
                    infowindow.open(map,marker);
                });

                map.setCenter(options.position);
            } else {
                map.setCenter(options.position);
                marker.setPosition(options.position);
            }
        }

        });
    }

    function loadMap() {
    var map = new google.maps.Map(document.getElementById("map"), {
        center: new google.maps.LatLng(51.527287, -0.103842),
        zoom: 15,
        mapTypeId: 'roadmap'
    });

        // Create a DIV to hold the control and call HomeControl()
    var currentLocationDiv = document.createElement('div');
    var currentLocation = new showCurrentLocation(currentLocationDiv, map);
    map.controls[google.maps.ControlPosition.TOP_CENTER].push(currentLocationDiv);

    downloadUrl("../../includes/university-map/source/overview_source.php", function(data) {
    var xml = data.responseXML;
    var markers = xml.documentElement.getElementsByTagName("marker");
    for (var i = 0; i < markers.length; i++) {
        var name = markers[i].getAttribute("name");
        var notes = markers[i].getAttribute("notes");
        var category = markers[i].getAttribute("category");
        var point = new google.maps.LatLng(
            parseFloat(markers[i].getAttribute("lat")),
            parseFloat(markers[i].getAttribute("lng")));
        var marker = createMarker(point, name, notes, category, map);
    }
    });
    }

    var infoWindow = new google.maps.InfoWindow({
        maxWidth: 400
    });

    function createMarker(point, name, notes, category, map) {
        var icon = customIcons[category] || {};
        var marker = new google.maps.Marker({
            map: map,
            position: point,
            icon: icon.icon,
            type: category
        });
        if (!markerGroups[category]) markerGroups[category] = [];
        markerGroups[category].push(marker);
        var html = "" + name + "
" + notes;
        bindInfoWindow(marker, map, infoWindow, html);
        return marker;
    }

    function toggleGroup(category) {
        for (var i = 0; i < markerGroups[category].length; i++) {
            var marker = markerGroups[category][i];
            if (!marker.getVisible()) {
                marker.setVisible(true);
            } else {
                marker.setVisible(false);
            }
        }
    }

    function bindInfoWindow(marker, map, infoWindow, html) {
        google.maps.event.addListener(marker, 'click', function() {
            infoWindow.setContent(html);
            infoWindow.open(map, marker);

        });
    }

    function downloadUrl(url, callback) {
        var request = window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();

        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                request.onreadystatechange = doNothing;
                callback(request, request.status);
            }
        };

        request.open('GET', url, true);
        request.send(null);
    }

    function doNothing() {}
<|endoftext|>


<commit_before>var http = require('http');
var cluster = require('cluster');
var express = require('express');
var cors = require('cors');
var bunyan = require('bunyan');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var superagent = require('superagent');

var flightApi = require('./routes/flights');

var config = require('./config');

var worker = 'unknown';
if (cluster.isWorker) {
  worker = cluster.worker.uniqueID;
  config.bunyan.worker = worker;
}
var log = bunyan.createLogger(config.bunyan);

// Pipe console printing to log file instead
// Logging will still show up in console as well.
console.error = log.error.bind(log);
console.log = log.info.bind(log);

// Setup mongodb
mongoose.connect(config.db);

var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);

app.use(bodyParser.json());
app.use(cors());

app.use('*', function(req, res, next) {
  res.setHeader('Content-Type', 'application/json');
  next();
});
app.use('/api', flightApi);

io.on('connection', function(socket) {
  socket.on('request-flight', function(data) {
    superagent.get([config.api, 'livestore', 'en', data.country, 'per-country', data.airport, data.from, data.to].join('/')+'?id=H4cK3r&currency=USD')
		.end(function(err, response) {
			if (err) {
				socket.emit('error', 'something went wrong in the socket');
        return;
			}
      var data = JSON.parse(response.text);
      var fares = data.fares;
      var airports = data.airports;
      if (fares.length) {
        //Check if we have travelled to country
        var travelInfo = {
          fromAirport: fares[0].a,
          destAirport: fares[0].b,
          price: fares[0].conv_fare,
          country: airports[fares[0].b],
          departure: fares[0].d1
        }

        socket.emit('new-flight', travelInfo);
      }
		});
  });
});

module.exports = server;
<commit_msg>Changing parameter names for easier reading<commit_after>var http = require('http');
var cluster = require('cluster');
var express = require('express');
var cors = require('cors');
var bunyan = require('bunyan');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var superagent = require('superagent');

var flightApi = require('./routes/flights');

var config = require('./config');

var worker = 'unknown';
if (cluster.isWorker) {
  worker = cluster.worker.uniqueID;
  config.bunyan.worker = worker;
}
var log = bunyan.createLogger(config.bunyan);

// Pipe console printing to log file instead
// Logging will still show up in console as well.
console.error = log.error.bind(log);
console.log = log.info.bind(log);

// Setup mongodb
mongoose.connect(config.db);

var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);

app.use(bodyParser.json());
app.use(cors());

app.use('*', function(req, res, next) {
  res.setHeader('Content-Type', 'application/json');
  next();
});
app.use('/api', flightApi);

io.on('connection', function(socket) {
  socket.on('request-flight', function(data) {
    superagent.get([
      config.api,
      'livestore',
      'en',
      data.country,
      'per-country',
      data.airport,
      data.from,
      data.to].join('/') +
      '?id=H4cK3r&currency=USD')
		.end(function(err, response) {
			if (err) {
				socket.emit('error', 'something went wrong in the socket');
        return;
			}
      var responseData = JSON.parse(response.text);
      var fares = responseData.fares;
      var airports = responseData.airports;
      if (fares.length) {
        // Check if we have travelled to country
        var travelInfo = {
          fromAirport: fares[0].a,
          destAirport: fares[0].b,
          price: fares[0].conv_fare,
          country: {
            airportName: airports[fares[0].b].a_i,
            city: airports[fares[0].b].a_n,
            countryCode: airports[fares[0].b].cc_c,
            countryName: airports[fares[0].b].cc_n,
            dontKnow: airports[fares[0].b].ci_n,
          },
          departure: fares[0].d1
        };

        socket.emit('new-flight', travelInfo);
      }
		});
  });
});

module.exports = server;
<|endoftext|>


<commit_before>import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { setLocale } from '../actions';
import Header from './Header';

@connect(
  state => ({
    app: state.app,
  }),
  dispatch => bindActionCreators({
    setLocale,
  }, dispatch)
)

export default class AppSettings extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { setLocale } = this.props;
    return (

<FormattedMessage id="settings" />

<FormattedMessage id="language" />

               setLocale('en')}>

English

               setLocale('cn')}>

简体中文

               setLocale('fr')}>

Français

               setLocale('de')}>

Deutsch

               setLocale('ru')}>

Русский

               setLocale('kr')}>

한국어

    );
  }
}
<commit_msg>Improve settings render<commit_after>import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { setLocale } from '../actions';
import Header from './Header';

@connect(
  state => ({
    app: state.app,
  }),
  dispatch => bindActionCreators({
    setLocale,
  }, dispatch)
)

export default class AppSettings extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { setLocale } = this.props;
    return (

<FormattedMessage id="settings" />

<FormattedMessage id="language" />

               setLocale('en')}>

English

               setLocale('cn')}>

简体中文

               setLocale('fr')}>

Français

               setLocale('de')}>

Deutsch

               setLocale('ru')}>

Русский

               setLocale('kr')}>

한국어

    );
  }
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var express = require('express');
var request = require('request');
var handlebars = require('handlebars');
var hbs = require('hbs');
var sendgrid = require('sendgrid');
var keys = require(__dirname + '/keys.js');

var app = express();

hbs.registerHelper('foreach', function(context, options) {
    var ret = "";
    for(var prop in context)
    {
        ret = ret + options.fn({
			property:prop, value:context[prop]
        });
    }
    return ret;
});

app.configure('development', function() {
	app.use(express.bodyParser());
	app.use(express.errorHandler());
	app.locals.pretty = true;

	app.set("view engine", 'tmpl');
	app.set("view options", { layout: false });

	app.engine('tmpl', require('hbs').__express);

	app.use(express.static(__dirname + '/public'));
});

app.get('/', function(req, res){
	if(req.query.token){
		//show page with selection of groups
		//maybe should be client-side js for loading the groups?
	} else {
		//send them a facebook auth screen - this could be done on the client side, too
	}
	res.send(__dirname + '/public/index.html');
});

app.get('/selection', function(req, res){
	if(req.query.token && req.query.fbid){
		request({json: true, url:'https://graph.facebook.com/me', qs:{'access_token':req.query.token}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var user_info = body;
				request({json: true, url:'https://graph.facebook.com/me/accounts', qs:{'access_token':req.query.token}}, function(error, response, body){
					if(!error && response.statusCode == 200){
						var data = {
							name: user_info.first_name,
							accessToken: req.query.token,
							pages: body.data
						};
						console.log(data);
						res.render('pages', data, function(err, html){
							if(err){
								console.log(err);
							} else {
								res.send(html);
							}
						});
					}
				});
			}
		});
	} else {
		res.redirect('/');
	}
});

app.get('/:pageid', function(req, res){
	if(req.query.token){
		request({json: true, url:'https://graph.facebook.com/me/accounts', qs:{'access_token':req.query.token}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var page;
				_.each(body.data, function(element){
					if(element.id == req.params.pageid){
						page = element;
					}
				});
				res.redirect('/' + page.id + '?pageToken=' + page.access_token);
			}
		});
	} else if(req.query.pageToken) {
		request({json: true, url:'https://graph.facebook.com/' + req.params.pageid, qs:{'access_token':req.query.pageToken}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var page = body;
				request({json: true, url:'https://graph.facebook.com/' + req.params.pageid + '/feed', qs:{'access_token':req.query.pageToken}}, function(error, response, body){
					var item = body.feed.data[0];
					request({method: 'post', url:'http://access.alchemyapi.com/calls/text/TextGetTextSentiment', body:{apikey:keys.ALCHEMY, text:item.message, outputMode:'json'}}, function(error, response, body){
						var sentiment = {
							type: body.docSentiment.type,
							value: body.docSentiment.score
						};
						var viewData = {
							post: item,
							sentiment: sentiment,
							page: page,
							name: 'Random User'
						};
						res.render('page', viewData, function(err, html){
							if(err){
								console.log(err);
							} else {
								res.send(html);
							}
						});
					});
				});
			}
		});
	} else {
		res.redirect('/');
	}
});

app.post('/:pageid', function(req, res){
	if(req.query.token){

	}
});

app.get('/:pageid/:postid', function(req, res){
	res.send('Nope');
});

app.listen(80);<commit_msg>URL change<commit_after>var _ = require('underscore');
var express = require('express');
var request = require('request');
var handlebars = require('handlebars');
var hbs = require('hbs');
var sendgrid = require('sendgrid');
var keys = require(__dirname + '/keys.js');

var app = express();

hbs.registerHelper('foreach', function(context, options) {
    var ret = "";
    for(var prop in context)
    {
        ret = ret + options.fn({
			property:prop, value:context[prop]
        });
    }
    return ret;
});

app.configure('development', function() {
	app.use(express.bodyParser());
	app.use(express.errorHandler());
	app.locals.pretty = true;

	app.set("view engine", 'tmpl');
	app.set("view options", { layout: false });

	app.engine('tmpl', require('hbs').__express);

	app.use(express.static(__dirname + '/public'));
});

app.get('/', function(req, res){
	if(req.query.token){
		//show page with selection of groups
		//maybe should be client-side js for loading the groups?
	} else {
		//send them a facebook auth screen - this could be done on the client side, too
	}
	res.send(__dirname + '/public/index.html');
});

app.get('/selection', function(req, res){
	if(req.query.token && req.query.fbid){
		request({json: true, url:'https://graph.facebook.com/me', qs:{'access_token':req.query.token}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var user_info = body;
				request({json: true, url:'https://graph.facebook.com/me/accounts', qs:{'access_token':req.query.token}}, function(error, response, body){
					if(!error && response.statusCode == 200){
						var data = {
							name: user_info.first_name,
							accessToken: req.query.token,
							pages: body.data
						};
						console.log(data);
						res.render('pages', data, function(err, html){
							if(err){
								console.log(err);
							} else {
								res.send(html);
							}
						});
					}
				});
			}
		});
	} else {
		res.redirect('/');
	}
});

app.get('/:pageid', function(req, res){
	if(req.query.token){
		request({json: true, url:'https://graph.facebook.com/me/accounts', qs:{'access_token':req.query.token}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var page;
				_.each(body.data, function(element){
					if(element.id == req.params.pageid){
						page = element;
					}
				});
				res.redirect('/' + page.id + '?pageToken=' + page.access_token);
			}
		});
	} else if(req.query.pageToken) {
		request({json: true, url:'https://graph.facebook.com/' + req.params.pageid, qs:{'access_token':req.query.pageToken}}, function(error, response, body){
			if(!error && response.statusCode == 200){
				var page = body;
				request({json: true, url:'https://graph.facebook.com/' + req.params.pageid + '?fields=feed', qs:{'access_token':req.query.pageToken}}, function(error, response, body){
					var item = body.feed.data[0];
					request({method: 'post', url:'http://access.alchemyapi.com/calls/text/TextGetTextSentiment', body:{apikey:keys.ALCHEMY, text:item.message, outputMode:'json'}}, function(error, response, body){
						var sentiment = {
							type: body.docSentiment.type,
							value: body.docSentiment.score
						};
						var viewData = {
							post: item,
							sentiment: sentiment,
							page: page,
							name: 'Random User'
						};
						res.render('page', viewData, function(err, html){
							if(err){
								console.log(err);
							} else {
								res.send(html);
							}
						});
					});
				});
			}
		});
	} else {
		res.redirect('/');
	}
});

app.post('/:pageid', function(req, res){
	if(req.query.token){

	}
});

app.get('/:pageid/:postid', function(req, res){
	res.send('Nope');
});

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


<commit_before>
var fbutil = require('./fbutil');

function SearchQueue(esc, reqRef, resRef, cleanupInterval) {
   this.esc = esc;
   this.inRef = reqRef;
   this.outRef = resRef;
   this.cleanupInterval = cleanupInterval;
   console.log('Queue started, IN: "%s", OUT: "%s"'.grey, fbutil.pathName(this.inRef), fbutil.pathName(this.outRef));
   setTimeout(function() {
      this.inRef.on('child_added', this._process, this);
   }.bind(this), 1000);
   this._nextInterval();
}

SearchQueue.prototype = {
   _process: function(snap) {
      var dat = snap.val();
      if( this._assertValidSearch(snap.name(), snap.val()) ) {
         // structure jquery into JSON object format expected by elasticsearch
         var queryObj = this._isJson(dat.query) ? JSON.parse(dat.query) : dat.query;
         queryObj = queryObj.hasOwnProperty('query') ? queryObj : { "query": queryObj };
         this.esc.search(dat.index, dat.type, queryObj, dat.options)
            .on('data', function(data) {
               console.log('search result', data);
               this._reply(snap.name(), JSON.parse(data));
            }.bind(this))
//         .on('done', function(){
            //always returns 0 right now
//         })
            .on('error', function(error){
               console.log(error);
               this._reply(snap.name(), {error: error, total: 0});
            }.bind(this))
            .exec();
      }
   },

   _reply: function(key, results) {
      if( results.error ) {
         this._replyError(key, results.error);
      }
      else {
         console.log('result %s: %d hits'.yellow, key, results.hits.total);
         this._send(key, results.hits);
      }
   },

   _assertValidSearch: function(key, props) {
      var res = true;
      if( typeof(props) !== 'object' || !props.index || !props.type || !props.query ) {
         this._replyError(key, 'search request must be a valid object with keys index, type, and query');
      }
      return res;
   },

   _replyError: function(key, err) {
      this._send(key, { total: 0, error: err })
   },

   _send: function(key, data) {
      this.inRef.child(key).remove(this._abortOnWriteError.bind(this));
      this.outRef.child(key).setWithPriority(data, new Date().valueOf());
   },

   _abortOnWriteError: function(err) {
      if( err ) {
         console.log((err+'').red);
         throw new Error('Unable to remove queue item, probably a security error? '+err);
      }
   },

   _housekeeping: function() {
      var self = this;
      // remove all responses which are older than CHECK_INTERVAL
      this.outRef.endAt(new Date().valueOf() - self.cleanupInterval).once('value', function(snap) {
         var count = snap.numChildren();
         if( count ) {
            console.warn('housekeeping: found %d orphans (removing them now) %s'.red, count, new Date());
            snap.forEach(function(ss) { ss.ref().remove(); });
         }
         self._nextInterval();
      });
   },

   _nextInterval: function() {
      var interval = this.cleanupInterval > 60000? 'minutes' : 'seconds';
      console.log('Next cleanup in %d %s'.grey, Math.round(this.cleanupInterval/(interval==='seconds'? 1000 : 60000)), interval);
      setTimeout(this._housekeeping.bind(this), this.cleanupInterval);
   },

   _isJson: function(str) {
       try {
           JSON.parse(str);
       } catch (e) {
           return false;
       }
       return true;
   }
};

exports.init = function(esc, url, reqPath, resPath, matchWholeWords, cleanupInterval) {
   new SearchQueue(esc, fbutil.fbRef(url, reqPath), fbutil.fbRef(url, resPath), matchWholeWords, cleanupInterval);
};
<commit_msg>Update SearchQueue.js<commit_after>
var fbutil = require('./fbutil');

function SearchQueue(esc, reqRef, resRef, cleanupInterval) {
   this.esc = esc;
   this.inRef = reqRef;
   this.outRef = resRef;
   this.cleanupInterval = cleanupInterval;
   console.log('Queue started, IN: "%s", OUT: "%s"'.grey, fbutil.pathName(this.inRef), fbutil.pathName(this.outRef));
   setTimeout(function() {
      this.inRef.on('child_added', this._process, this);
   }.bind(this), 1000);
   this._nextInterval();
}

SearchQueue.prototype = {
   _process: function(snap) {
      var dat = snap.val();
      if( this._assertValidSearch(snap.key(), snap.val()) ) {
         // structure jquery into JSON object format expected by elasticsearch
         var queryObj = this._isJson(dat.query) ? JSON.parse(dat.query) : dat.query;
         queryObj = queryObj.hasOwnProperty('query') ? queryObj : { "query": queryObj };
         this.esc.search(dat.index, dat.type, queryObj, dat.options)
            .on('data', function(data) {
               console.log('search result', data);
               this._reply(snap.key(), JSON.parse(data));
            }.bind(this))
//         .on('done', function(){
            //always returns 0 right now
//         })
            .on('error', function(error){
               console.log(error);
               this._reply(snap.key(), {error: error, total: 0});
            }.bind(this))
            .exec();
      }
   },

   _reply: function(key, results) {
      if( results.error ) {
         this._replyError(key, results.error);
      }
      else {
         console.log('result %s: %d hits'.yellow, key, results.hits.total);
         this._send(key, results.hits);
      }
   },

   _assertValidSearch: function(key, props) {
      var res = true;
      if( typeof(props) !== 'object' || !props.index || !props.type || !props.query ) {
         this._replyError(key, 'search request must be a valid object with keys index, type, and query');
      }
      return res;
   },

   _replyError: function(key, err) {
      this._send(key, { total: 0, error: err })
   },

   _send: function(key, data) {
      this.inRef.child(key).remove(this._abortOnWriteError.bind(this));
      this.outRef.child(key).setWithPriority(data, new Date().valueOf());
   },

   _abortOnWriteError: function(err) {
      if( err ) {
         console.log((err+'').red);
         throw new Error('Unable to remove queue item, probably a security error? '+err);
      }
   },

   _housekeeping: function() {
      var self = this;
      // remove all responses which are older than CHECK_INTERVAL
      this.outRef.endAt(new Date().valueOf() - self.cleanupInterval).once('value', function(snap) {
         var count = snap.numChildren();
         if( count ) {
            console.warn('housekeeping: found %d orphans (removing them now) %s'.red, count, new Date());
            snap.forEach(function(ss) { ss.ref().remove(); });
         }
         self._nextInterval();
      });
   },

   _nextInterval: function() {
      var interval = this.cleanupInterval > 60000? 'minutes' : 'seconds';
      console.log('Next cleanup in %d %s'.grey, Math.round(this.cleanupInterval/(interval==='seconds'? 1000 : 60000)), interval);
      setTimeout(this._housekeeping.bind(this), this.cleanupInterval);
   },

   _isJson: function(str) {
       try {
           JSON.parse(str);
       } catch (e) {
           return false;
       }
       return true;
   }
};

exports.init = function(esc, url, reqPath, resPath, matchWholeWords, cleanupInterval) {
   new SearchQueue(esc, fbutil.fbRef(url, reqPath), fbutil.fbRef(url, resPath), matchWholeWords, cleanupInterval);
};
<|endoftext|>


<commit_before>var extend = require('./utils').extend;
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var http = require('http');
var url = require('url');
var crypto = require('crypto');
var WebSocketConnection = require('./WebSocketConnection');

const INIT = -1;
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;

var ID_COUNTER = 0;

var protocolSeparators = [
	"(", ")", "<", ">", "@",
	",", ";", ":", "\\", "\"",
	"/", "[", "]", "?", "=",
	"{", "}", " ", String.fromCharCode(9)
];

function WebSocketClient(config) {
    // TODO: Implement extensions

    this.config = {
        // 1MiB max frame size.
        maxReceivedFrameSize: 0x100000,

        // 8MiB max message size, only applicable if
        // assembleFragments is true
        maxReceivedMessageSize: 0x800000,

        // Outgoing messages larger than fragmentationThreshold will be
        // split into multiple fragments.
        fragmentOutgoingMessages: true,

        // Outgoing frames are fragmented if they exceed this threshold.
        // Default is 16KiB
        fragmentationThreshold: 0x4000,

        // If true, fragmented messages will be automatically assembled
        // and the full message will be emitted via a 'message' event.
        // If false, each frame will be emitted via a 'frame' event and
        // the application will be responsible for aggregating multiple
        // fragmented frames.  Single-frame messages will emit a 'message'
        // event in addition to the 'frame' event.
        // Most users will want to leave this set to 'true'
        assembleFragments: true,

        // The Nagle Algorithm makes more efficient use of network resources
        // by introducing a small delay before sending small packets so that
        // multiple messages can be batched together before going onto the
        // wire.  This however comes at the cost of latency, so the default
        // is to disable it.  If you don't need low latency and are streaming
        // lots of small messages, you can change this to 'false'
        disableNagleAlgorithm: true,

        // The number of milliseconds to wait after sending a close frame
        // for an acknowledgement to come back before giving up and just
        // closing the socket.
        closeTimeout: 5000
    };
    if (config) {
        extend(this.config, config);
    }

    this.readyState = INIT;
};

util.inherits(WebSocketClient, EventEmitter);

WebSocketClient.prototype.connect = function(requestUrl, protocols, origin) {
    var s = this;
    if (!(protocols instanceof Array)) {
        protocols = [];
    }
    this.protocols = protocols;
    this.origin = origin;

    if (typeof(requestUrl) === 'string') {
        this.url = url.parse(requestUrl);
    }
    else {
        this.url = requestUrl; // in case an already parsed url is passed in.
    }
    if (!this.url.protocol) {
        throw new Error("You must specify a full WebSocket URL, including protocol.");
    }
    if (!this.url.host) {
        throw new Error("You must specify a full WebSocket URL, including hostname.  Relative URLs are not supported.");
    }

    this.secure = (this.url.protocol === 'wss:');

    // validate protocol characters:
    this.protocols.forEach(function(protocol, index, array) {
        for (var i=0; i < protocol.length; i ++) {
            var charCode = protocol.charCodeAt(i);
            var character = protocol.charAt(i);
            if (charCode < 0x0021 || charCode > 0x007E || protocolSeparators.indexOf(character) !== -1) {
                throw new Error("Protocol list contains invalid character '" + String.fromCharCode(charCode) + "'");
            }
        }
    });

    var defaultPorts = {
        'ws:': '80',
        'wss:': '443'
    };

    if (!this.url.port) {
        this.url.port = defaultPorts[this.url.protocol];
    }

    var nonce = new Buffer(16);
    for (var i=0; i < 16; i++) {
        nonce[i] = Math.round(Math.random()*0xFF);
    }
    this.base64nonce = nonce.toString('base64');

    var requestOptions = {
        host: this.url.hostname,
        port: this.url.port,
        method: 'GET',
        path: this.url.path
    };

    var hostHeaderValue = this.url.hostname;
    if ((this.url.protocol === 'ws:' && this.url.port !== '80') ||
        (this.url.protocol === 'wss:' && this.url.port !== '443'))  {
        hostHeaderValue += (":" + this.url.port)
    }

    // FIXME: Using old http.createClient interface since Node's new
    // Agent-based API is buggy.
    var client = this.httpClient = http.createClient(this.url.port, this.url.hostname);
    var reqHeaders = {
        'Upgrade': 'websocket',
        'Connection': 'Upgrade',
        'Sec-WebSocket-Version': '8',
        'Sec-WebSocket-Key': this.base64nonce,
        'Host': hostHeaderValue
    };
    if (this.protocols.length > 0) {
        reqHeaders['Sec-WebSocket-Protocol'] = this.protocols.join(', ');
    }
    if (this.origin) {
        reqHeaders['Sec-WebSocket-Origin'] = this.origin;
    }
    // TODO: Implement extensions

    var handleRequestError = function(error) {
        s.emit('error', error);
    }
    client.on('error', handleRequestError);

    client.on('upgrade', function handleClientUpgrade(response, socket, head) {
        req.removeListener('error', handleRequestError);
        s.socket = socket;
        s.response = response;
        s.firstDataChunk = head;
        s.validateHandshake();
    });

    var req = this.request = client.request(this.url.path, reqHeaders);

    req.on('response', function(response) {
        s.failHandshake("Server responded with a non-101 status: " + response.statusCode);
    });

    req.end();
};

WebSocketClient.prototype.validateHandshake = function() {
    var headers = this.response.headers;

    if (this.protocols.length > 0) {
        this.protocol = headers['sec-websocket-protocol'];
        if (this.protocol) {
            if (this.protocols.indexOf(this.protocol) === -1) {
                this.failHandshake("Server did not respond with a requested protocol.");
                return;
            }
        }
        else {
            this.failHandshake("Expected a Sec-WebSocket-Protocol header.");
            return;
        }
    }

    if (!(headers['connection'] && headers['connection'].toLocaleLowerCase() === 'upgrade')) {
        this.failHandshake("Expected a Connection: Upgrade header from the server");
        return;
    }

    if (!(headers['upgrade'] && headers['upgrade'].toLocaleLowerCase() === 'websocket')) {
        this.failHandshake("Expected an Upgrade: websocket header from the server");
        return;
    }

    var sha1 = crypto.createHash('sha1');
    sha1.update(this.base64nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    var expectedKey = sha1.digest('base64');

    if (!headers['sec-websocket-accept']) {
        this.failHandshake("Expected Sec-WebSocket-Accept header from server")
        return;
    }

    if (!(headers['sec-websocket-accept'] === expectedKey)) {
        this.failHandshake("Sec-WebSocket-Accept header from server didn't match expected value of " + expectedKey);
        return;
    }

    // TODO: Support extensions

    this.succeedHandshake();
};

WebSocketClient.prototype.failHandshake = function(errorDescription) {
    if (this.socket && this.socket.writable) {
        this.socket.end();
    }
    this.emit('connectFailed', errorDescription);
};

WebSocketClient.prototype.succeedHandshake = function() {
    var connection = new WebSocketConnection(this.socket, [], this.protocol, true, this.config);
    this.emit('connect', connection);
    if (this.firstDataChunk.length > 0) {
        connection.handleSocketData(this.firstDataChunk);
        this.firstDataChunk = null;
    }
};

module.exports = WebSocketClient;<commit_msg>changed instances of this.url.path to this.url.pathname, this being the correct fieldname resulting from a url.parse call. Now the client sends the correct path to the server<commit_after>var extend = require('./utils').extend;
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var http = require('http');
var url = require('url');
var crypto = require('crypto');
var WebSocketConnection = require('./WebSocketConnection');

const INIT = -1;
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;

var ID_COUNTER = 0;

var protocolSeparators = [
	"(", ")", "<", ">", "@",
	",", ";", ":", "\\", "\"",
	"/", "[", "]", "?", "=",
	"{", "}", " ", String.fromCharCode(9)
];

function WebSocketClient(config) {
    // TODO: Implement extensions

    this.config = {
        // 1MiB max frame size.
        maxReceivedFrameSize: 0x100000,

        // 8MiB max message size, only applicable if
        // assembleFragments is true
        maxReceivedMessageSize: 0x800000,

        // Outgoing messages larger than fragmentationThreshold will be
        // split into multiple fragments.
        fragmentOutgoingMessages: true,

        // Outgoing frames are fragmented if they exceed this threshold.
        // Default is 16KiB
        fragmentationThreshold: 0x4000,

        // If true, fragmented messages will be automatically assembled
        // and the full message will be emitted via a 'message' event.
        // If false, each frame will be emitted via a 'frame' event and
        // the application will be responsible for aggregating multiple
        // fragmented frames.  Single-frame messages will emit a 'message'
        // event in addition to the 'frame' event.
        // Most users will want to leave this set to 'true'
        assembleFragments: true,

        // The Nagle Algorithm makes more efficient use of network resources
        // by introducing a small delay before sending small packets so that
        // multiple messages can be batched together before going onto the
        // wire.  This however comes at the cost of latency, so the default
        // is to disable it.  If you don't need low latency and are streaming
        // lots of small messages, you can change this to 'false'
        disableNagleAlgorithm: true,

        // The number of milliseconds to wait after sending a close frame
        // for an acknowledgement to come back before giving up and just
        // closing the socket.
        closeTimeout: 5000
    };
    if (config) {
        extend(this.config, config);
    }

    this.readyState = INIT;
};

util.inherits(WebSocketClient, EventEmitter);

WebSocketClient.prototype.connect = function(requestUrl, protocols, origin) {
    var s = this;
    if (!(protocols instanceof Array)) {
        protocols = [];
    }
    this.protocols = protocols;
    this.origin = origin;

    if (typeof(requestUrl) === 'string') {
        this.url = url.parse(requestUrl);
    }
    else {
        this.url = requestUrl; // in case an already parsed url is passed in.
    }
    if (!this.url.protocol) {
        throw new Error("You must specify a full WebSocket URL, including protocol.");
    }
    if (!this.url.host) {
        throw new Error("You must specify a full WebSocket URL, including hostname.  Relative URLs are not supported.");
    }

    this.secure = (this.url.protocol === 'wss:');

    // validate protocol characters:
    this.protocols.forEach(function(protocol, index, array) {
        for (var i=0; i < protocol.length; i ++) {
            var charCode = protocol.charCodeAt(i);
            var character = protocol.charAt(i);
            if (charCode < 0x0021 || charCode > 0x007E || protocolSeparators.indexOf(character) !== -1) {
                throw new Error("Protocol list contains invalid character '" + String.fromCharCode(charCode) + "'");
            }
        }
    });

    var defaultPorts = {
        'ws:': '80',
        'wss:': '443'
    };

    if (!this.url.port) {
        this.url.port = defaultPorts[this.url.protocol];
    }

    var nonce = new Buffer(16);
    for (var i=0; i < 16; i++) {
        nonce[i] = Math.round(Math.random()*0xFF);
    }
    this.base64nonce = nonce.toString('base64');

    var requestOptions = {
        host: this.url.hostname,
        port: this.url.port,
        method: 'GET',
        path: this.url.pathname
    };

    var hostHeaderValue = this.url.hostname;
    if ((this.url.protocol === 'ws:' && this.url.port !== '80') ||
        (this.url.protocol === 'wss:' && this.url.port !== '443'))  {
        hostHeaderValue += (":" + this.url.port)
    }

    // FIXME: Using old http.createClient interface since Node's new
    // Agent-based API is buggy.
    var client = this.httpClient = http.createClient(this.url.port, this.url.hostname);
    var reqHeaders = {
        'Upgrade': 'websocket',
        'Connection': 'Upgrade',
        'Sec-WebSocket-Version': '8',
        'Sec-WebSocket-Key': this.base64nonce,
        'Host': hostHeaderValue
    };
    if (this.protocols.length > 0) {
        reqHeaders['Sec-WebSocket-Protocol'] = this.protocols.join(', ');
    }
    if (this.origin) {
        reqHeaders['Sec-WebSocket-Origin'] = this.origin;
    }
    // TODO: Implement extensions

    var handleRequestError = function(error) {
        s.emit('error', error);
    }
    client.on('error', handleRequestError);

    client.on('upgrade', function handleClientUpgrade(response, socket, head) {
        req.removeListener('error', handleRequestError);
        s.socket = socket;
        s.response = response;
        s.firstDataChunk = head;
        s.validateHandshake();
    });

    var req = this.request = client.request(this.url.pathname, reqHeaders);

    req.on('response', function(response) {
        s.failHandshake("Server responded with a non-101 status: " + response.statusCode);
    });

    req.end();
};

WebSocketClient.prototype.validateHandshake = function() {
    var headers = this.response.headers;

    if (this.protocols.length > 0) {
        this.protocol = headers['sec-websocket-protocol'];
        if (this.protocol) {
            if (this.protocols.indexOf(this.protocol) === -1) {
                this.failHandshake("Server did not respond with a requested protocol.");
                return;
            }
        }
        else {
            this.failHandshake("Expected a Sec-WebSocket-Protocol header.");
            return;
        }
    }

    if (!(headers['connection'] && headers['connection'].toLocaleLowerCase() === 'upgrade')) {
        this.failHandshake("Expected a Connection: Upgrade header from the server");
        return;
    }

    if (!(headers['upgrade'] && headers['upgrade'].toLocaleLowerCase() === 'websocket')) {
        this.failHandshake("Expected an Upgrade: websocket header from the server");
        return;
    }

    var sha1 = crypto.createHash('sha1');
    sha1.update(this.base64nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    var expectedKey = sha1.digest('base64');

    if (!headers['sec-websocket-accept']) {
        this.failHandshake("Expected Sec-WebSocket-Accept header from server")
        return;
    }

    if (!(headers['sec-websocket-accept'] === expectedKey)) {
        this.failHandshake("Sec-WebSocket-Accept header from server didn't match expected value of " + expectedKey);
        return;
    }

    // TODO: Support extensions

    this.succeedHandshake();
};

WebSocketClient.prototype.failHandshake = function(errorDescription) {
    if (this.socket && this.socket.writable) {
        this.socket.end();
    }
    this.emit('connectFailed', errorDescription);
};

WebSocketClient.prototype.succeedHandshake = function() {
    var connection = new WebSocketConnection(this.socket, [], this.protocol, true, this.config);
    this.emit('connect', connection);
    if (this.firstDataChunk.length > 0) {
        connection.handleSocketData(this.firstDataChunk);
        this.firstDataChunk = null;
    }
};

module.exports = WebSocketClient;<|endoftext|>


<commit_before>( function( $ ) {
    /* private */
    function buildHTML( data, group ) {
        var at = "",
            desc = "Description",
            notices = "",
            latest = "",
            subs = "members",
            html,
            uid = new Date().getTime();

        // Redefine some things if we're dealing with @user
        if ( !group ) {
            at   = "@";
            desc = "Bio";
            subs = "subscribers";
            notices = "" +
                    "" + data.statuses_count + " notices," +
                    "";
            latest = "
Latest
" +
                "" + data.status.text + "";
        }

        html = "
" +
                  "
" +
                    "
" + ( data.name || data.fullname ) + "
" +
                    "" + at + ( data.screen_name || data.fullname ) +
                      "" +
                    "" +
                    "
" + notices +
                      "" +
                        "" + ( data.friends_count || data.member_count ) + " " + subs +
                      "" +
                    "
" +
                    "
" + desc + "
" +
                    "" + ( data.description || "" ) + "" + latest +
                  "
" +
                  "
subscribe" +
                  "
" +
                    "<form>" +
                      "<fieldset>" +
                        "Your Accound ID" +
                        "<input id='hc-profile-" + uid + "' type='text' placeholder='e.g. user@identi.ca' />" +
                        "<input type='hidden' name='profile' value='" + ( data.statusnet_profile_url || data.url ) + "' />" +
                        "<button type='submit'>Subscribe</button>" +
                      "</fieldset>" +
                    "</form>" +
                  "
" +
                "
" +
              "
";

        return html;
    }

    function buildCard( data, $link ) {
        $link.on( "mouseenter", function() {
            setTimeout( function() {
                $( ".gs-hovercard" ).hide(); /* Only show one hovercard at a time */
                $link.data( "gs-hovercard" ).show();
            }, 400 );
        } );

        var offset = $link.offset(),
            group  = ( $link.text().charAt( 0 ) === "!" ),
            html   = buildHTML( data, group );

        html = $( html ).appendTo( "body" )
            .on( "mouseleave", function() {
                setTimeout( function() {
                    html.hide();
                }, 400 );
            } )
            .css( { top: offset.top, left: offset.left } );

        html.find( ".hc-follow" ).on( "click", function( e ) {
            e.preventDefault();
            $( this ).parent().find( ".hc-follow-form" ).slideToggle();
        } );

        html.find( "form" ).on( "submit", function( e ) {
            e.preventDefault();

            var $form   = $( this ),
                $input  = $form.find( "input[type=text]" ),
                profile = $input.val().split( "@" );

            if ( profile.length !== 2 ) {
                /* TODO: show error msg */
                $input.css( "border", "1px solid #f00" );
            } else {
                profile = profile[ 1 ];
                if ( !group ) {
                    $form.attr( "action", "http://" + profile + "/main/ostatussub" );
                } else {
                    $form.attr( "action", "http://" + profile + "/main/ostatusgroup" );
                }
                this.submit();
            }
        } );

        $link.data( "gs-hovercard", html ); /* Could use ARIA instead */
        $link.trigger( "mouseenter" );      /* Initial show() after we got the data */
    }

    function buildErrorCard() {
       /* TODO */
    }

    function getData() {
        var $this = $( this ),
            id    = $this.text().substr( 1 ),
            group = ( $this.text().charAt( 0 ) === "!" ),
            patt  = group ? new RegExp( "group\/" + id + "$" ) : new RegExp( id + "$" ),
            url   = $this.attr( "href" ).replace( patt, "" ),
            api   = group ? "/api/statusnet/groups/show.json?id=" + id : "/api/users/show.json?id=" + id;

        // NOTE:  Doesn't support api at non-default locations
        $.getJSON( url + api ) /* Fancy URL */
            .success( function( data ) {
                buildCard( data, $this );
            } )
            .error( function() { /* Try non-fancy URL */
                $.getJSON( url + "/index.php" + api )
                    .success( function( data ) {
                        buildCard( data, $this );
                    } )
                    .error( function() {
                        buildErrorCard();
                    } );
            } );
    }

    // public
    var methods = {
        init: function() {
            return this.each( function() {
                var $this = $( this ),
                    $collection;

                if ( $this.is( "a[href]" ) ) {
                    $collection = $this;
                } else {
                    $collection = $this.find( "a[href]" );
                }

                if ( $collection ) {
                    $collection.each( function() {
                        if ( $( this ).text().match( /@\w+/ ) ) {
                            $( this ).one( "mouseenter", getData );
                        } else if ( $( this ).text().match( /!\w+/ ) ) {
                            $( this ).one( "mouseenter", getData );
                        }
                    } );
                }
            } );
        }
    };

    // Method calling logic
    $.fn.gsHovercard = function( method ) {
        var elm;

        if ( methods[ method ] ) {
            elm = methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) );
        } else if ( typeof method === "object" || !method ) {
            elm = methods.init.apply( this, arguments );
        } else {
            $.error( "Method " +  method + " does not exist on jQuery.gsHovercard" );
        }

        return elm;
    };

}( jQuery ) );
<commit_msg>Placeholder: user@identi.ca -> user@example.org<commit_after>( function( $ ) {
    /* private */
    function buildHTML( data, group ) {
        var at = "",
            desc = "Description",
            notices = "",
            latest = "",
            subs = "members",
            html,
            uid = new Date().getTime();

        // Redefine some things if we're dealing with @user
        if ( !group ) {
            at   = "@";
            desc = "Bio";
            subs = "subscribers";
            notices = "" +
                    "" + data.statuses_count + " notices," +
                    "";
            latest = "
Latest
" +
                "" + data.status.text + "";
        }

        html = "
" +
                  "
" +
                    "
" + ( data.name || data.fullname ) + "
" +
                    "" + at + ( data.screen_name || data.fullname ) +
                      "" +
                    "" +
                    "
" + notices +
                      "" +
                        "" + ( data.friends_count || data.member_count ) + " " + subs +
                      "" +
                    "
" +
                    "
" + desc + "
" +
                    "" + ( data.description || "" ) + "" + latest +
                  "
" +
                  "
subscribe" +
                  "
" +
                    "<form>" +
                      "<fieldset>" +
                        "Your Accound ID" +
                        "<input id='hc-profile-" + uid + "' type='text' placeholder='e.g. user@example.org' />" +
                        "<input type='hidden' name='profile' value='" + ( data.statusnet_profile_url || data.url ) + "' />" +
                        "<button type='submit'>Subscribe</button>" +
                      "</fieldset>" +
                    "</form>" +
                  "
" +
                "
" +
              "
";

        return html;
    }

    function buildCard( data, $link ) {
        $link.on( "mouseenter", function() {
            setTimeout( function() {
                $( ".gs-hovercard" ).hide(); /* Only show one hovercard at a time */
                $link.data( "gs-hovercard" ).show();
            }, 400 );
        } );

        var offset = $link.offset(),
            group  = ( $link.text().charAt( 0 ) === "!" ),
            html   = buildHTML( data, group );

        html = $( html ).appendTo( "body" )
            .on( "mouseleave", function() {
                setTimeout( function() {
                    html.hide();
                }, 400 );
            } )
            .css( { top: offset.top, left: offset.left } );

        html.find( ".hc-follow" ).on( "click", function( e ) {
            e.preventDefault();
            $( this ).parent().find( ".hc-follow-form" ).slideToggle();
        } );

        html.find( "form" ).on( "submit", function( e ) {
            e.preventDefault();

            var $form   = $( this ),
                $input  = $form.find( "input[type=text]" ),
                profile = $input.val().split( "@" );

            if ( profile.length !== 2 ) {
                /* TODO: show error msg */
                $input.css( "border", "1px solid #f00" );
            } else {
                profile = profile[ 1 ];
                if ( !group ) {
                    $form.attr( "action", "http://" + profile + "/main/ostatussub" );
                } else {
                    $form.attr( "action", "http://" + profile + "/main/ostatusgroup" );
                }
                this.submit();
            }
        } );

        $link.data( "gs-hovercard", html ); /* Could use ARIA instead */
        $link.trigger( "mouseenter" );      /* Initial show() after we got the data */
    }

    function buildErrorCard() {
       /* TODO */
    }

    function getData() {
        var $this = $( this ),
            id    = $this.text().substr( 1 ),
            group = ( $this.text().charAt( 0 ) === "!" ),
            patt  = group ? new RegExp( "group\/" + id + "$" ) : new RegExp( id + "$" ),
            url   = $this.attr( "href" ).replace( patt, "" ),
            api   = group ? "/api/statusnet/groups/show.json?id=" + id : "/api/users/show.json?id=" + id;

        // NOTE:  Doesn't support api at non-default locations
        $.getJSON( url + api ) /* Fancy URL */
            .success( function( data ) {
                buildCard( data, $this );
            } )
            .error( function() { /* Try non-fancy URL */
                $.getJSON( url + "/index.php" + api )
                    .success( function( data ) {
                        buildCard( data, $this );
                    } )
                    .error( function() {
                        buildErrorCard();
                    } );
            } );
    }

    // public
    var methods = {
        init: function() {
            return this.each( function() {
                var $this = $( this ),
                    $collection;

                if ( $this.is( "a[href]" ) ) {
                    $collection = $this;
                } else {
                    $collection = $this.find( "a[href]" );
                }

                if ( $collection ) {
                    $collection.each( function() {
                        if ( $( this ).text().match( /@\w+/ ) ) {
                            $( this ).one( "mouseenter", getData );
                        } else if ( $( this ).text().match( /!\w+/ ) ) {
                            $( this ).one( "mouseenter", getData );
                        }
                    } );
                }
            } );
        }
    };

    // Method calling logic
    $.fn.gsHovercard = function( method ) {
        var elm;

        if ( methods[ method ] ) {
            elm = methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ) );
        } else if ( typeof method === "object" || !method ) {
            elm = methods.init.apply( this, arguments );
        } else {
            $.error( "Method " +  method + " does not exist on jQuery.gsHovercard" );
        }

        return elm;
    };

}( jQuery ) );
<|endoftext|>