<commit_before>var math = require('../math/math.js');
var Camera = require('./camera.js');
var EventTarget = require('./events.js');
var KEYCODES = require('../utility/keycodes.js');

var Vector = math.Vector;
var Matrix = math.Matrix;

/**
 * @constructor
 * @this {Scene}
 * @param {{canvas_id: string, width: number, height: number}} options
 */
function Scene(options){
    /** @type {number} */
    this.width = options.width;
    /** @type {number} */
    this.height = options.height;
    this.canvas = document.getElementById(options.canvas_id);
    this.ctx = this.canvas.getContext('2d');
    this._back_buffer = document.createElement('canvas');
    this._back_buffer.width = this.width;
    this._back_buffer.height = this.height;
    this._back_buffer_ctx = this._back_buffer.getContext('2d');
    this._back_buffer_image = null;
    this._depth_buffer = [];
    this.drawing_mode = 1;
    this.camera = new Camera(this.width, this.height);
    this.illumination = new Vector(90,0,0);
    /** @type {Array.<Mesh>} */
    this.meshes = [];
    /** @type {Object.<number, boolean>} */
    this._keys = {}; // Keys currently pressed
    this._key_count = 0; // Number of keys being pressed... this feels kludgy
    /** @type {?number} */
    this._anim_id = null;
    /** @type {boolean} */
    this._needs_update = true;
    this._draw_mode = 'wireframe';
    this.init();
}
Scene.prototype = new EventTarget();
/** @method */
Scene.prototype.init = function(){
    this.canvas.tabIndex = 1; // Set tab index to allow canvas to have focus to receive key events
    this._x_offset = Math.round(this.width / 2);
    this._y_offset = Math.round(this.height / 2);
    this.initializeDepthBuffer();
    this._back_buffer_image = this._back_buffer_ctx.createImageData(this.width, this.height);
    this.canvas.addEventListener('keydown', this.onKeyDown.bind(this), false);
    this.canvas.addEventListener('keyup', this.onKeyUp.bind(this), false);
    this.canvas.addEventListener('blur', this.emptyKeys.bind(this), false);
    EventTarget.call(this);
    this.update();
};
/**
 * Dump all pressed keys on blur.
 * @method
 */
Scene.prototype.emptyKeys = function(){
    this._key_count = 0;
    this._keys = {};
};
/** @method */
Scene.prototype.isKeyDown = function(key){
    return (KEYCODES[key] in this._keys);
};
/** @method */
Scene.prototype.onKeyDown = function(e){
    var pressed = e.keyCode || e.which;
    if (!this.isKeyDown(pressed)){
        this._key_count += 1;
        this._keys[pressed] = true;
    }
};
/** @method */
Scene.prototype.onKeyUp = function(e){
    var pressed = e.keyCode || e.which;
    if (pressed in this._keys){
        this._key_count -= 1;
        delete this._keys[pressed];
    }
};
Scene.prototype.initializeDepthBuffer = function(){
    for (var x = 0, len = this.width * this.height; x < len; x++){
        this._depth_buffer[x] = 9999999;
    }
};
/** @method */
Scene.prototype.drawPixel = function(x, y, z, color){
    x = Math.floor(x + this._x_offset);
    y = Math.floor(y + this._y_offset);
    if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
        var index = x + (y * this.width);
        if (z < this._depth_buffer[index]) {
            var image_data = this._back_buffer_image.data;
            var i = index * 4;
            image_data[i] = color.r;
            image_data[i+1] = color.g;
            image_data[i+2] = color.b;
            image_data[i+3] = 255;
            this._depth_buffer[index] = z;
        }
    }
};
/** @method  */
Scene.prototype.drawEdge = function(vector1, vector2, color){
    var abs = Math.abs;
    var current_x = vector1.x;
    var current_y = vector1.y;
    var current_z = vector1.z;
    var longest_dist = Math.max(abs(vector2.x - vector1.x), abs(vector2.y - vector1.y), abs(vector2.z - vector1.z));
    var step_x = (vector2.x - vector1.x) / longest_dist;
    var step_y = (vector2.y - vector1.y) / longest_dist;
    var step_z = (vector2.z - vector1.z) / longest_dist;

    for (var i = 0; i < longest_dist; i++){
        this.drawPixel(current_x, current_y, current_z, color);
        current_x += step_x;
        current_y += step_y;
        current_z += step_z;
    }
};
/** @method */
Scene.prototype.drawTriangle = function(vector1, vector2, vector3, color){
    this.drawEdge(vector1, vector2, color);
    this.drawEdge(vector2, vector3, color);
    this.drawEdge(vector3, vector1, color);
};
/** @method */
Scene.prototype.drawFlatBottomTriangle = function(v1, v2, v3, color){
    // Draw left to right
    if (v2.x >= v3.x){
        var temp = v3;
        v3 = v2;
        v2 = temp;
    }
    // compute deltas
    var dxy_left  = (v3.x-v1.x)/(v3.y-v1.y);
    var dxy_right = (v2.x-v1.x)/(v2.y-v1.y);
    var z_slope_left = (v3.z-v1.z)/(v3.y-v1.y);
    var z_slope_right = (v2.z-v1.z)/(v2.y-v1.y);

    // set starting and ending points for edge trace
    var xs = new Vector(v1.x, v1.y, v1.z);
    var xe = new Vector(v1.x, v1.y, v1.z);
    xs.z = v3.z + ((v1.y - v3.y) * z_slope_left);
    xe.z = v2.z + ((v1.y - v2.y) * z_slope_right);

    // draw each scanline
    for (var y=v1.y; y <= v2.y; y++){
        xs.y = y;
        xe.y = y;
        this.drawEdge(xs, xe, color);

        // move down one scanline
        xs.x+=dxy_left;
        xe.x+=dxy_right;
        xs.z+=z_slope_left;
        xe.z+=z_slope_right;
    }
};
Scene.prototype.drawFlatTopTriangle = function(v1, v2, v3, color){
    // Draw left to right
    if (v1.x >= v2.x){
        var temp = v1;
        v1 = v2;
        v2 = temp;
    }
    // compute deltas
    var dxy_left  = (v3.x-v1.x)/(v3.y-v1.y);
    var dxy_right = (v3.x-v2.x)/(v3.y-v2.y);
    var z_slope_left = (v3.z-v1.z)/(v3.y-v1.y);
    var z_slope_right = (v3.z-v2.z)/(v3.y-v2.y);

    // set starting and ending points for edge trace
    var xs = new Vector(v1.x, v1.y, v1.z);
    var xe = new Vector(v2.x, v1.y, v1.z);

    xs.z = v1.z + ((v1.y - v1.y) * z_slope_left);
    xe.z = v2.z + ((v1.y - v2.y) * z_slope_right);

    // draw each scanline
    for (var y=v1.y; y <= v3.y; y++){
        xs.y = y;
        xe.y = y;
        // draw a line from xs to xe at y in color c
        this.drawEdge(xs, xe, color);
        // move down one scanline
        xs.x+=dxy_left;
        xe.x+=dxy_right;
        xs.z+=z_slope_left;
        xe.z+=z_slope_right;
    }
    // Round right edge
};
/** @method */
Scene.prototype.fillTriangle = function(v1, v2, v3, color){
    // Draw edges first
    // TODO: Fix. This is a hack.
    //this.drawTriangle(v1, v2, v3, color);
    // Sort vertices by y value
    var temp;
    if(v1.y > v2.y) {
        temp = v2;
        v2 = v1;
        v1 = temp;
    }
    if(v2.y > v3.y) {
        temp = v2;
        v2 = v3;
        v3 = temp;
    }
    if(v1.y > v2.y) {
        temp = v2;
        v2 = v1;
        v1 = temp;
    }
    // Triangle with no height
    if ((v1.y - v3.y) === 0){
        return;
    }

    var short_slope, long_slope;
    if ((v2.y - v1.y) === 0) {
        short_slope = 0;
    } else {
        short_slope = (v2.x - v1.x) / (v2.y - v1.y);
    }
    if ((v3.y - v1.y) === 0) {
        long_slope = 0;
    } else {
        long_slope = (v3.x - v1.x) / (v3.y - v1.y);
    }

    if (v2.y === v3.y){
        // Flat top
        this.drawFlatBottomTriangle(v1, v2, v3, color);
    }
    else if (v1.y === v2.y ){
        // Flat bottom
        this.drawFlatTopTriangle(v1, v2, v3, color);
    } else {
        // Interpolate
        // Decompose into flat top
        var z_slope = (v3.z - v1.z) / (v3.y - v1.y);
        var x = ((v2.y - v1.y)*long_slope) + v1.x;
        var z = ((v2.y - v1.y)*z_slope) + v1.z;
        var v4 = new Vector(x, v2.y, z);
        this.drawFlatBottomTriangle(v1, v2, v4, color);
        this.drawFlatTopTriangle(v2, v4, v3, color);
    }
};
/** @method */
Scene.prototype.renderScene = function(){
    // TODO: Simplify this function.
    this._back_buffer_image = this._back_buffer_ctx.createImageData(this.width, this.height);
    this.initializeDepthBuffer();
    var camera_matrix = this.camera.view_matrix;
    var projection_matrix = this.camera.perspectiveFov;
    var light = this.illumination;
    for (var i = 0, len = this.meshes.length; i < len; i++){
        var mesh = this.meshes[i];
        var scale = mesh.scale;
        var rotation = mesh.rotation;
        var position = mesh.position;
        var world_matrix = Matrix.scale(scale.x, scale.y, scale.z).multiply(
            Matrix.rotation(rotation.pitch, rotation.yaw, rotation.roll).multiply(
                Matrix.translation(position.x, position.y, position.z)));
        var wvp_matrix = world_matrix.multiply(camera_matrix).multiply(projection_matrix);
        for (var k = 0; k < mesh.faces.length; k++){
            var face = mesh.faces[k].face;
            var color = mesh.faces[k].color;
            var v1 = mesh.vertices[face[0]];
            var v2 = mesh.vertices[face[1]];
            var v3 = mesh.vertices[face[2]];
            var wv1 = v1.transform(wvp_matrix);
            var wv2 = v2.transform(wvp_matrix);
            var wv3 = v3.transform(wvp_matrix);
            var normal = mesh.normal(k).transform(world_matrix).normalize();
            var light_direction = light.subtract(v1.transform(world_matrix)).normalize();
            var illumination_angle = normal.dot(light_direction);
            // TODO: Backface culling, if not in wireframe mode
            color = color.lighten(illumination_angle/4);
            this.fillTriangle(wv1, wv2, wv3, color.rgb);
        }
    }
    this._back_buffer_ctx.putImageData(this._back_buffer_image, 0, 0);
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.drawImage(this._back_buffer, 0, 0, this.canvas.width, this.canvas.height);
};
/** @method */
Scene.prototype.addMesh = function(mesh){
    this.meshes.push(mesh);
};
/** @method */
Scene.prototype.removeMesh = function(mesh){
    // TODO: Write this
    return mesh;
};
/** @method */
Scene.prototype.update = function(){
    if (this._key_count > 0){
        this.fire('keydown');
    }
    if (this._needs_update) {
        this.renderScene();
        this._needs_update = false;
    }
    this._anim_id = window.requestAnimationFrame(this.update.bind(this));
};

module.exports = Scene;
<commit_msg>Addd backface and (naive) fustrum culling<commit_after>var math = require('../math/math.js');
var Camera = require('./camera.js');
var EventTarget = require('./events.js');
var KEYCODES = require('../utility/keycodes.js');

var Vector = math.Vector;
var Matrix = math.Matrix;

/**
 * @constructor
 * @this {Scene}
 * @param {{canvas_id: string, width: number, height: number}} options
 */
function Scene(options){
    /** @type {number} */
    this.width = options.width;
    /** @type {number} */
    this.height = options.height;
    this.canvas = document.getElementById(options.canvas_id);
    this.ctx = this.canvas.getContext('2d');
    this._back_buffer = document.createElement('canvas');
    this._back_buffer.width = this.width;
    this._back_buffer.height = this.height;
    this._back_buffer_ctx = this._back_buffer.getContext('2d');
    this._back_buffer_image = null;
    this._depth_buffer = [];
    this.drawing_mode = 1;
    this._backface_culling = true;
    this.camera = new Camera(this.width, this.height);
    this.illumination = new Vector(90,0,0);
    /** @type {Array.<Mesh>} */
    this.meshes = [];
    /** @type {Object.<number, boolean>} */
    this._keys = {}; // Keys currently pressed
    this._key_count = 0; // Number of keys being pressed... this feels kludgy
    /** @type {?number} */
    this._anim_id = null;
    /** @type {boolean} */
    this._needs_update = true;
    this._draw_mode = 'wireframe';
    this.init();
}
Scene.prototype = new EventTarget();
/** @method */
Scene.prototype.init = function(){
    this.canvas.tabIndex = 1; // Set tab index to allow canvas to have focus to receive key events
    this._x_offset = Math.round(this.width / 2);
    this._y_offset = Math.round(this.height / 2);
    this.initializeDepthBuffer();
    this._back_buffer_image = this._back_buffer_ctx.createImageData(this.width, this.height);
    this.canvas.addEventListener('keydown', this.onKeyDown.bind(this), false);
    this.canvas.addEventListener('keyup', this.onKeyUp.bind(this), false);
    this.canvas.addEventListener('blur', this.emptyKeys.bind(this), false);
    EventTarget.call(this);
    this.update();
};
/**
 * Dump all pressed keys on blur.
 * @method
 */
Scene.prototype.emptyKeys = function(){
    this._key_count = 0;
    this._keys = {};
};
/** @method */
Scene.prototype.isKeyDown = function(key){
    return (KEYCODES[key] in this._keys);
};
/** @method */
Scene.prototype.onKeyDown = function(e){
    var pressed = e.keyCode || e.which;
    if (!this.isKeyDown(pressed)){
        this._key_count += 1;
        this._keys[pressed] = true;
    }
};
/** @method */
Scene.prototype.onKeyUp = function(e){
    var pressed = e.keyCode || e.which;
    if (pressed in this._keys){
        this._key_count -= 1;
        delete this._keys[pressed];
    }
};
/** @method */
Scene.prototype.initializeDepthBuffer = function(){
    for (var x = 0, len = this.width * this.height; x < len; x++){
        this._depth_buffer[x] = 9999999;
    }
};
/** @method */
Scene.prototype.offscreen = function(vector){
    var x = vector.x + this._x_offset;
    var y = vector.y + this._y_offset;
    return (x < 0 || x > this.width || y < 0 || y > this.height);
};
/** @method */
Scene.prototype.drawPixel = function(x, y, z, color){
    x = Math.floor(x + this._x_offset);
    y = Math.floor(y + this._y_offset);
    if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
        var index = x + (y * this.width);
        if (z < this._depth_buffer[index]) {
            var image_data = this._back_buffer_image.data;
            var i = index * 4;
            image_data[i] = color.r;
            image_data[i+1] = color.g;
            image_data[i+2] = color.b;
            image_data[i+3] = 255;
            this._depth_buffer[index] = z;
        }
    }
};
/** @method  */
Scene.prototype.drawEdge = function(vector1, vector2, color){
    var abs = Math.abs;
    var current_x = vector1.x;
    var current_y = vector1.y;
    var current_z = vector1.z;
    var longest_dist = Math.max(abs(vector2.x - vector1.x), abs(vector2.y - vector1.y), abs(vector2.z - vector1.z));
    var step_x = (vector2.x - vector1.x) / longest_dist;
    var step_y = (vector2.y - vector1.y) / longest_dist;
    var step_z = (vector2.z - vector1.z) / longest_dist;

    for (var i = 0; i < longest_dist; i++){
        this.drawPixel(current_x, current_y, current_z, color);
        current_x += step_x;
        current_y += step_y;
        current_z += step_z;
    }
};
/** @method */
Scene.prototype.drawTriangle = function(vector1, vector2, vector3, color){
    this.drawEdge(vector1, vector2, color);
    this.drawEdge(vector2, vector3, color);
    this.drawEdge(vector3, vector1, color);
};
/** @method */
Scene.prototype.drawFlatBottomTriangle = function(v1, v2, v3, color){
    // Draw left to right
    if (v2.x >= v3.x){
        var temp = v3;
        v3 = v2;
        v2 = temp;
    }
    // compute deltas
    var dxy_left  = (v3.x-v1.x)/(v3.y-v1.y);
    var dxy_right = (v2.x-v1.x)/(v2.y-v1.y);
    var z_slope_left = (v3.z-v1.z)/(v3.y-v1.y);
    var z_slope_right = (v2.z-v1.z)/(v2.y-v1.y);

    // set starting and ending points for edge trace
    var xs = new Vector(v1.x, v1.y, v1.z);
    var xe = new Vector(v1.x, v1.y, v1.z);
    xs.z = v3.z + ((v1.y - v3.y) * z_slope_left);
    xe.z = v2.z + ((v1.y - v2.y) * z_slope_right);

    // draw each scanline
    for (var y=v1.y; y <= v2.y; y++){
        xs.y = y;
        xe.y = y;
        this.drawEdge(xs, xe, color);

        // move down one scanline
        xs.x+=dxy_left;
        xe.x+=dxy_right;
        xs.z+=z_slope_left;
        xe.z+=z_slope_right;
    }
};
Scene.prototype.drawFlatTopTriangle = function(v1, v2, v3, color){
    // Draw left to right
    if (v1.x >= v2.x){
        var temp = v1;
        v1 = v2;
        v2 = temp;
    }
    // compute deltas
    var dxy_left  = (v3.x-v1.x)/(v3.y-v1.y);
    var dxy_right = (v3.x-v2.x)/(v3.y-v2.y);
    var z_slope_left = (v3.z-v1.z)/(v3.y-v1.y);
    var z_slope_right = (v3.z-v2.z)/(v3.y-v2.y);

    // set starting and ending points for edge trace
    var xs = new Vector(v1.x, v1.y, v1.z);
    var xe = new Vector(v2.x, v1.y, v1.z);

    xs.z = v1.z + ((v1.y - v1.y) * z_slope_left);
    xe.z = v2.z + ((v1.y - v2.y) * z_slope_right);

    // draw each scanline
    for (var y=v1.y; y <= v3.y; y++){
        xs.y = y;
        xe.y = y;
        // draw a line from xs to xe at y in color c
        this.drawEdge(xs, xe, color);
        // move down one scanline
        xs.x+=dxy_left;
        xe.x+=dxy_right;
        xs.z+=z_slope_left;
        xe.z+=z_slope_right;
    }
    // Round right edge
};
/** @method */
Scene.prototype.fillTriangle = function(v1, v2, v3, color){
    // Draw edges first
    // TODO: Fix. This is a hack.
    //this.drawTriangle(v1, v2, v3, color);
    // Sort vertices by y value
    var temp;
    if(v1.y > v2.y) {
        temp = v2;
        v2 = v1;
        v1 = temp;
    }
    if(v2.y > v3.y) {
        temp = v2;
        v2 = v3;
        v3 = temp;
    }
    if(v1.y > v2.y) {
        temp = v2;
        v2 = v1;
        v1 = temp;
    }
    // Triangle with no height
    if ((v1.y - v3.y) === 0){
        return;
    }

    var short_slope, long_slope;
    if ((v2.y - v1.y) === 0) {
        short_slope = 0;
    } else {
        short_slope = (v2.x - v1.x) / (v2.y - v1.y);
    }
    if ((v3.y - v1.y) === 0) {
        long_slope = 0;
    } else {
        long_slope = (v3.x - v1.x) / (v3.y - v1.y);
    }

    if (v2.y === v3.y){
        // Flat top
        this.drawFlatBottomTriangle(v1, v2, v3, color);
    }
    else if (v1.y === v2.y ){
        // Flat bottom
        this.drawFlatTopTriangle(v1, v2, v3, color);
    } else {
        // Interpolate
        // Decompose into flat top
        var z_slope = (v3.z - v1.z) / (v3.y - v1.y);
        var x = ((v2.y - v1.y)*long_slope) + v1.x;
        var z = ((v2.y - v1.y)*z_slope) + v1.z;
        var v4 = new Vector(x, v2.y, z);
        this.drawFlatBottomTriangle(v1, v2, v4, color);
        this.drawFlatTopTriangle(v2, v4, v3, color);
    }
};
/** @method */
Scene.prototype.renderScene = function(){
    // TODO: Simplify this function.
    this._back_buffer_image = this._back_buffer_ctx.createImageData(this.width, this.height);
    this.initializeDepthBuffer();
    var camera_matrix = this.camera.view_matrix;
    var projection_matrix = this.camera.perspectiveFov;
    var light = this.illumination;
    for (var i = 0, len = this.meshes.length; i < len; i++){
        var mesh = this.meshes[i];
        var scale = mesh.scale;
        var rotation = mesh.rotation;
        var position = mesh.position;
        var world_matrix = Matrix.scale(scale.x, scale.y, scale.z).multiply(
            Matrix.rotation(rotation.pitch, rotation.yaw, rotation.roll).multiply(
                Matrix.translation(position.x, position.y, position.z)));
        var wvp_matrix = world_matrix.multiply(camera_matrix).multiply(projection_matrix);
        for (var k = 0; k < mesh.faces.length; k++){
            var face = mesh.faces[k].face;
            var color = mesh.faces[k].color;
            var v1 = mesh.vertices[face[0]];
            var v2 = mesh.vertices[face[1]];
            var v3 = mesh.vertices[face[2]];
            var vv1 = v1.transform(world_matrix); // Vertex1 in world space
            var wv1 = v1.transform(wvp_matrix);
            var wv2 = v2.transform(wvp_matrix);
            var wv3 = v3.transform(wvp_matrix);
            var normal = mesh.normal(k).transform(world_matrix).normalize();
            var draw = false;
            if (this._backface_culling) {
                if (this.camera.position.subtract(vv1).dot(normal) > 0){
                    draw = true;
                } else {
                    draw = false;
                }
            }
            // TODO: Fix frustum culling
            // This is really stupid frustum culling... this can result in some faces not being
            // drawn when they should, e.g. when a triangles vertices straddle the frustrum.
            if (draw && this.offscreen(wv1) && this.offscreen(wv2) && this.offscreen(wv3)){
                draw = false;
            }
            if (draw){
                var light_direction = light.subtract(vv1).normalize();
                var illumination_angle = normal.dot(light_direction);
                color = color.lighten(illumination_angle/4);
                this.fillTriangle(wv1, wv2, wv3, color.rgb);
                //this.drawTriangle(wv1, wv2, wv3, color.rgb);
            }

        }
    }
    this._back_buffer_ctx.putImageData(this._back_buffer_image, 0, 0);
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.drawImage(this._back_buffer, 0, 0, this.canvas.width, this.canvas.height);
};
/** @method */
Scene.prototype.addMesh = function(mesh){
    this.meshes.push(mesh);
};
/** @method */
Scene.prototype.removeMesh = function(mesh){
    // TODO: Write this
    return mesh;
};
/** @method */
Scene.prototype.update = function(){
    if (this._key_count > 0){
        this.fire('keydown');
    }
    if (this._needs_update) {
        this.renderScene();
        this._needs_update = false;
    }
    this._anim_id = window.requestAnimationFrame(this.update.bind(this));
};

module.exports = Scene;
<|endoftext|>


<commit_before>import m from 'mithril';
import prop from 'mithril/stream';
import _ from 'underscore';
import h from '../h';
import projectReminder from './project-reminder';
import projectVM from '../vms/project-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.project_sidebar');
const projectTabs = {
    oninit: function(vnode) {
        const fixedNavClass = 'project-nav-fixed',
            isFixed = prop(false),
            originalPosition = prop(-1),
            project = vnode.attrs.project;

        const fixOnScroll = el => () => {
            const viewportOffset = el.getBoundingClientRect();

            if (window.scrollY <= originalPosition() && isFixed()) {
                originalPosition(-1);
                isFixed(false);
                el.classList.remove(fixedNavClass);
            }

            if (viewportOffset.top < 0 || (window.scrollY > originalPosition() && originalPosition() > 0)) {
                if (!isFixed()) {
                    originalPosition(window.scrollY);
                    isFixed(true);
                    el.classList.add(fixedNavClass);
                }
            }
        };

        const navDisplay = localVnode => {
            const fixNavBar = fixOnScroll(localVnode.dom);
            window.addEventListener('scroll', fixNavBar);
        };

        const navigate = (event) => {
            event.preventDefault();

            if (projectVM.isSubscription(project)) {
                h.navigateTo(`/projects/${project().project_id}/subscriptions/start`);
                return false;
            }

            h.navigateTo(`/projects/${project().project_id}/contributions/new`);

            return false;
        };

        vnode.state = {
            navDisplay,
            isFixed,
            navigate
        };
    },
    view: function({state, attrs}) {
        const project = attrs.project,
            rewards = attrs.rewardDetails;

        return m('nav-wrapper', { style: attrs.style }, project() ? [
            m('.w-section.project-nav', {
                oncreate: state.navDisplay
            }, [
                m('.w-container', [
                    m('.w-row', [
                        m('.w-col.w-col-8', [!_.isEmpty(rewards()) ?
                            m(`a[id="rewards-link"][class="w-hidden-main w-hidden-medium dashboard-nav-link mf  ${(h.hashMatch('#rewards') || (h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="/${project().permalink}#rewards"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_rewards_view', project: project() })
                            }, 'Recompensas') : m(`a[id="rewards-link"][class="w-hidden-main w-hidden-medium dashboard-nav-link mf ${(h.hashMatch('#contribution_suggestions') || (h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="/${project().permalink}#contribution_suggestions"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_contribsuggestions_view', project: project() })
                            }, 'Valores Sugeridos'),
                            m(`a[id="about-link"][class="dashboard-nav-link mf ${(h.hashMatch('#about') || (!h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="#about"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_about_view', project: project() })
                            }, 'Sobre'),
                            m(`a[id="posts-link"][class="dashboard-nav-link mf ${(h.hashMatch('#posts') ? 'selected' : '')}"][href="/${project().permalink}#posts"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_posts_view', project: project() })
                            }, [
                                'Novidades ',
                                m('span.badge', project() ? project().posts_count : '')
                            ]),
                            m(`a[id="contributions-link"][class="w-hidden-small w-hidden-tiny dashboard-nav-link mf ${(h.hashMatch('#contributions') ? 'selected' : '')}"][href="#contributions"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_contributions_view', project: project() })
                            }, projectVM.isSubscription(project) ? [
                                'Assinantes ',
                                m('span.badge.w-hidden-small.w-hidden-tiny', attrs.subscriptionData() ? attrs.subscriptionData().total_subscriptions : '-')
                            ] : [
                                'Apoiadores ',
                                m('span.badge.w-hidden-small.w-hidden-tiny', project() ? project().total_contributors : '-')
                            ]
                            ),
                            m(`a[id="comments-link"][class="dashboard-nav-link mf ${(h.hashMatch('#comments') ? 'selected' : '')}"][href="#comments"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_comments_view', project: project() })
                            }, [
                                'Comentários ',
                                project() ? m(`fb:comments-count[href="http://www.catarse.me/${project().permalink}"][class="badge project-fb-comment w-hidden-small w-hidden-tiny"][style="display: inline"]`, m.trust(' ')) : '-'
                            ]),
                        ]),
                        project() ? m('.w-col.w-col-4.w-hidden-small.w-hidden-tiny', project().open_for_contributions && !attrs.hasSubscription() ? [
                            m('.w-row.project-nav-back-button', [
                                projectVM.isSubscription(project) ? m('.w-col.w-col-12', [
                                    m(`a.w-button.btn[href="/projects/${project().project_id}/subscriptions/start"]`, {
                                        onclick: h.analytics.event({ cat: 'contribution_create', act: 'contribution_floatingbtn_click', project: project() }, state.navigate)
                                    }, window.I18n.t(`submit_${project().mode}`, I18nScope()))
                                ]) : m('.w-col.w-col-6.w-col-medium-8', [
                                    m(`a.w-button.btn[href="/projects/${project().project_id}/contributions/new"]`, {
                                        onclick: h.analytics.event({ cat: 'contribution_create', act: 'contribution_floatingbtn_click', project: project() })
                                    }, 'Apoiar este projeto')
                                ]),
                                m('.w-col.w-col-6.w-col-medium-4', {
                                    onclick: h.analytics.event({ cat: 'project_view', act: 'project_floatingreminder_click', project: project() })
                                }, [
                                    projectVM.isSubscription(project) ? null : m(projectReminder, { project, type: 'button', hideTextOnMobile: true })
                                ])
                            ])
                        ] : '') : ''
                    ])
                ])
            ]),
            (state.isFixed() && !project().is_owner_or_admin) ? m('.w-section.project-nav') : ''
        ] : '');
    }
};

export default projectTabs;
<commit_msg>format project tabs code<commit_after>import m from 'mithril';
import prop from 'mithril/stream';
import _ from 'underscore';
import h from '../h';
import projectReminder from './project-reminder';
import projectVM from '../vms/project-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.project_sidebar');
const projectTabs = {
    oninit: function(vnode) {
        const fixedNavClass = 'project-nav-fixed',
            isFixed = prop(false),
            originalPosition = prop(-1),
            project = vnode.attrs.project;

        const fixOnScroll = el => () => {
            const viewportOffset = el.getBoundingClientRect();

            if (window.scrollY <= originalPosition() && isFixed()) {
                originalPosition(-1);
                isFixed(false);
                el.classList.remove(fixedNavClass);
            }

            if (viewportOffset.top < 0 || (window.scrollY > originalPosition() && originalPosition() > 0)) {
                if (!isFixed()) {
                    originalPosition(window.scrollY);
                    isFixed(true);
                    el.classList.add(fixedNavClass);
                }
            }
        };

        const navDisplay = localVnode => {
            const fixNavBar = fixOnScroll(localVnode.dom);
            window.addEventListener('scroll', fixNavBar);
        };

        const navigate = (event) => {
            event.preventDefault();

            if (projectVM.isSubscription(project)) {
                h.navigateTo(`/projects/${project().project_id}/subscriptions/start`);
                return false;
            }

            h.navigateTo(`/projects/${project().project_id}/contributions/new`);

            return false;
        };

        vnode.state = {
            navDisplay,
            isFixed,
            navigate
        };
    },
    view: function({state, attrs}) {
        const project = attrs.project,
            rewards = attrs.rewardDetails;

        return m('nav-wrapper', { style: attrs.style }, project() ? [
            m('.w-section.project-nav', {
                oncreate: state.navDisplay
            }, [
                m('.w-container', [
                    m('.w-row', [
                        m('.w-col.w-col-8', [
                            !_.isEmpty(rewards()) ?
                                m(`a[id="rewards-link"][class="w-hidden-main w-hidden-medium dashboard-nav-link mf  ${(h.hashMatch('#rewards') || (h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="/${project().permalink}#rewards"]`, {
                                    style: 'float: left;',
                                    onclick: h.analytics.event({
                                        cat: 'project_view', act: 'project_rewards_view', project: project() })
                                }, 'Recompensas')
                                :
                                m(`a[id="rewards-link"][class="w-hidden-main w-hidden-medium dashboard-nav-link mf ${(h.hashMatch('#contribution_suggestions') || (h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="/${project().permalink}#contribution_suggestions"]`, {
                                    style: 'float: left;',
                                    onclick: h.analytics.event({
                                        cat: 'project_view', act: 'project_contribsuggestions_view', project: project() })
                                }, 'Valores Sugeridos'),
                            m(`a[id="about-link"][class="dashboard-nav-link mf ${(h.hashMatch('#about') || (!h.mobileScreen() && h.hashMatch('')) ? 'selected' : '')}"][href="#about"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_about_view', project: project() })
                            }, 'Sobre'),
                            m(`a[id="posts-link"][class="dashboard-nav-link mf ${(h.hashMatch('#posts') ? 'selected' : '')}"][href="/${project().permalink}#posts"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_posts_view', project: project() })
                            }, [
                                'Novidades ',
                                m('span.badge', project() ? project().posts_count : '')
                            ]),
                            m(`a[id="contributions-link"][class="w-hidden-small w-hidden-tiny dashboard-nav-link mf ${(h.hashMatch('#contributions') ? 'selected' : '')}"][href="#contributions"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_contributions_view', project: project() })
                            }, projectVM.isSubscription(project) ? [
                                'Assinantes ',
                                m('span.badge.w-hidden-small.w-hidden-tiny', attrs.subscriptionData() ? attrs.subscriptionData().total_subscriptions : '-')
                            ] : [
                                'Apoiadores ',
                                m('span.badge.w-hidden-small.w-hidden-tiny', project() ? project().total_contributors : '-')
                            ]
                            ),
                            m(`a[id="comments-link"][class="dashboard-nav-link mf ${(h.hashMatch('#comments') ? 'selected' : '')}"][href="#comments"]`, {
                                style: 'float: left;',
                                onclick: h.analytics.event({
                                    cat: 'project_view', act: 'project_comments_view', project: project() })
                            }, [
                                'Comentários ',
                                project() ? m(`fb:comments-count[href="http://www.catarse.me/${project().permalink}"][class="badge project-fb-comment w-hidden-small w-hidden-tiny"][style="display: inline"]`, m.trust(' ')) : '-'
                            ]),
                        ]),
                        project() ? m('.w-col.w-col-4.w-hidden-small.w-hidden-tiny', project().open_for_contributions && !attrs.hasSubscription() ? [
                            m('.w-row.project-nav-back-button', [
                                projectVM.isSubscription(project) ? m('.w-col.w-col-12', [
                                    m(`a.w-button.btn[href="/projects/${project().project_id}/subscriptions/start"]`, {
                                        onclick: h.analytics.event({ cat: 'contribution_create', act: 'contribution_floatingbtn_click', project: project() }, state.navigate)
                                    }, window.I18n.t(`submit_${project().mode}`, I18nScope()))
                                ]) : m('.w-col.w-col-6.w-col-medium-8', [
                                    m(`a.w-button.btn[href="/projects/${project().project_id}/contributions/new"]`, {
                                        onclick: h.analytics.event({ cat: 'contribution_create', act: 'contribution_floatingbtn_click', project: project() })
                                    }, 'Apoiar este projeto')
                                ]),
                                m('.w-col.w-col-6.w-col-medium-4', {
                                    onclick: h.analytics.event({ cat: 'project_view', act: 'project_floatingreminder_click', project: project() })
                                }, [
                                    projectVM.isSubscription(project) ? null : m(projectReminder, { project, type: 'button', hideTextOnMobile: true })
                                ])
                            ])
                        ] : '') : ''
                    ])
                ])
            ]),
            (state.isFixed() && !project().is_owner_or_admin) ? m('.w-section.project-nav') : ''
        ] : '');
    }
};

export default projectTabs;
<|endoftext|>


<commit_before>'use strict';

var Utils = {
  promisify: function (array, query, document) {
    var promises = [];

    if (query && !document) {
      document = query;
    }

    array.forEach(function (promisedCallback) {
      promises.push(promisedCallback(document));
    });

    if (array.length <= 0) {
      promises.push(new Promise(function(resolve, reject) {
        reject = reject || null; // to avoid jshint unused code
        return resolve(document);
      }));
    }

    return promises;
  },

  setDatetime: function (opName, datetime, document) {
    if (datetime) {
      if (opName === 'update') {
        if (!document.$set && !document.$setOnInsert) {
          document.updatedAt = new Date();
          document.createdAt = new Date();
        } else {
          document.$set = document.$set || {};
          document.$set.updatedAt = new Date();

          document.$setOnInsert = document.$setOnInsert || {};
          document.$setOnInsert.createdAt = new Date();
        }
      } else if (opName === 'insert') {
        document.createdAt = new Date();
        document.updatedAt = new Date();
      }
    }
    return document;
  },

  setParams: function (opName, query, sort, docToProcess, options) {
    var params = [];

    switch(opName) {
    case 'update':
      params.push(query, docToProcess, options);
      break;
    case 'findAndModify':
      params.push(query, sort, docToProcess, options);
      break;
    default:
      params.push(docToProcess, options);
      break;
    }

    return params;
  },

  processUpdatedDocment: function (opName, collection, isAfterHook, query, mongObject, options) {
    if (opName === 'update' && mongObject.result.ok && isAfterHook) {
      var objToReturn = {
        value: [],
        lastErrorObject: {}
      };

      return collection.find(query)
      .toArray()
      .then(function (updatedDocs) {
        if (!updatedDocs.length) {
          return mongObject;
        }
        objToReturn.value = (options && options.multi && mongObject.result.n > 1) ?
          updatedDocs : updatedDocs[0];

        objToReturn.lastErrorObject.updatedExisting = (mongObject.result.nModified) ? true: false;

        objToReturn.lastErrorObject.n = mongObject.result.n;

        if (!mongObject.result.nModified && mongObject.result.upserted) {
          objToReturn.lastErrorObject.upserted = updatedDocs[0]._id;
        }

        objToReturn.ok = mongObject.result.ok;

        return objToReturn;
      });
    } else {
      /*jshint unused: false*/
      return new Promise(function (resolve, reject) {
        resolve(mongObject);
      });
    }
  },

  getHooksTemplate: function () {
    return {
      before: { insert: [], update: [], remove: [] },
      after: { insert: [], update: [], remove: [] }
    };
  }
};

// export Utils helper
module.exports = Utils;<commit_msg>Add hasProperty method<commit_after>'use strict';

var Utils = {
  promisify: function (array, query, document) {
    var promises = [];

    if (query && !document) {
      document = query;
    }

    array.forEach(function (promisedCallback) {
      promises.push(promisedCallback(document));
    });

    if (array.length <= 0) {
      promises.push(new Promise(function(resolve, reject) {
        reject = reject || null; // to avoid jshint unused code
        return resolve(document);
      }));
    }

    return promises;
  },

  hasProperty: function(array, toFind) {
    return Object.keys(array).some(function (key) {
      return ~key.indexOf(toFind);
    });
  },

  setDatetime: function (opName, datetime, document) {
    if (datetime) {
      if (opName === 'update') {
        if (!document.$set && !document.$setOnInsert) {
          document.updatedAt = new Date();
          document.createdAt = new Date();
        } else {
          document.$set = document.$set || {};
          document.$set.updatedAt = new Date();

          document.$setOnInsert = document.$setOnInsert || {};
          document.$setOnInsert.createdAt = new Date();
        }
      } else if (opName === 'insert') {
        document.createdAt = new Date();
        document.updatedAt = new Date();
      }
    }
    return document;
  },

  setParams: function (opName, query, sort, docToProcess, options) {
    var params = [];

    switch(opName) {
    case 'update':
      params.push(query, docToProcess, options);
      break;
    case 'findAndModify':
      params.push(query, sort, docToProcess, options);
      break;
    default:
      params.push(docToProcess, options);
      break;
    }

    return params;
  },

  processUpdatedDocment: function (opName, collection, isAfterHook, query, mongObject, options) {
    if (opName === 'update' && mongObject.result.ok && isAfterHook) {
      var objToReturn = {
        value: [],
        lastErrorObject: {}
      };

      return collection.find(query)
      .toArray()
      .then(function (updatedDocs) {
        if (!updatedDocs.length) {
          return mongObject;
        }
        objToReturn.value = (options && options.multi && mongObject.result.n > 1) ?
          updatedDocs : updatedDocs[0];

        objToReturn.lastErrorObject.updatedExisting = (mongObject.result.nModified) ? true: false;

        objToReturn.lastErrorObject.n = mongObject.result.n;

        if (!mongObject.result.nModified && mongObject.result.upserted) {
          objToReturn.lastErrorObject.upserted = updatedDocs[0]._id;
        }

        objToReturn.ok = mongObject.result.ok;

        return objToReturn;
      });
    } else {
      /*jshint unused: false*/
      return new Promise(function (resolve, reject) {
        resolve(mongObject);
      });
    }
  },

  getHooksTemplate: function () {
    return {
      before: { insert: [], update: [], remove: [] },
      after: { insert: [], update: [], remove: [] }
    };
  }
};

// export Utils helper
module.exports = Utils;<|endoftext|>


<commit_before>// Wee 2.0.5 (weepower.com)
// Licensed under Apache 2 (http://www.apache.org/licenses/LICENSE-2.0)
// DO NOT MODIFY THIS FILE

Wee.fn.make('data', {
	// Make Ajax request based on specified options
	request: function(opt) {
		var conf = Wee.$extend({
				args: [],
				data: {}
			}, opt),
			x = new XMLHttpRequest();

		x.onreadystatechange = function() {
			if (x.readyState === 4) {
				if (x.status >= 200 && x.status < 400) {
					if (conf.success) {
						var resp = x.responseText,
							orig = resp;

						// Parse JSON response if specified
						if (conf.json || conf.template) {
							try {
								resp = JSON.parse(resp);
							} catch (e) {
								resp = {};
							}

							if (conf.template) {
								resp = Wee.data.parse(conf.template, resp);
								conf.args.unshift(orig);
							}
						}

						conf.args.unshift(resp);

						// Execute success callback if specified
						Wee.$exec(conf.success, {
							args: conf.args,
							scope: conf.scope
						});

						return true;
					}
				} else {
					if (conf.failure) {
						Wee.$exec(conf.failure, {
							scope: conf.scope
						});
					}

					return false;
				}
			}
		};

		// Post or get endpoint based on specification
		if (conf.method == 'post') {
			x.open('POST', conf.url, true);
			x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
			x.send(Wee.$isObject(conf.data) ? Wee.$serialize(conf.data) : conf.data);
		} else {
			if (conf.cache === false) {
				var dt = new Date().getTime();
				conf.data.dt = dt;
			}

			if (Object.keys(conf.data).length > 0) {
				conf.url += '?' + Wee.$serialize(conf.data);
			}

			x.open('GET', conf.url, true);
			x.send(null);
		}
	},
	// Parse specified data into specified template string
	// Return string
	parse: function(str, obj, opt) {
		opt = opt || {};
		obj = obj || {};

		var scope = this;

		// Make {{template}} variable replacements
		return str.replace(/{{([^}]*)}}/g, function(str, key) {
			return scope.$private('replace', obj, key, opt);
		});
	}
}, {
	clean: function(str) {
		return str
			.replace(/&/g, '&')
			.replace(/&/g, '&')
			.replace(/</g, '<')
			.replace(/>/g, '>')
			.replace(/"/g, '"');
	},
	replace: function(obj, key, opt) {
		var segs = key.split('||'),
			resp = segs[0].trim().split('.'),
			len = resp.length - 1,
			i = 0;

		for (; i <= len; i++) {
			key = resp[i];
			obj = obj[key];

			if (i === len && typeof obj == 'string') {
				return opt.encode === true ? this.clean(obj) : obj;
			}
		}

		return segs.length > 1 ? segs[1].trim() : '';
	}
});<commit_msg>Add new JSONP options to request Add new request config parameter to define headers Add new advanced template parsing logic<commit_after>Wee.fn.make('data', {
	// Make Ajax request based on specified options
	request: function(opt) {
		var conf = Wee.$extend({
				args: [],
				data: {}
			}, opt);

		if (conf.cache === false) {
			conf.data.dt = new Date().getTime();
		}

		if (conf.jsonp) {
			var head = Wee._doc.getElementsByTagName('head')[0];

			if (conf.success) {
				var v = this.$get('v', 1),
					func = 'jsonp' + v;

				Wee._win[func] = function(data) {
					conf.args.unshift(data)

					Wee.$exec(conf.success, {
						args: conf.args,
						scope: conf.scope
					});
				};

				conf.data[conf.jsonp === true ? 'callback' : conf.jsonp] = func;

				this.$set('v', v + 1);
			}

			if (Object.keys(conf.data).length > 0) {
				conf.url += '?' + Wee.$serialize(conf.data);
			}

			var el = Wee._doc.createElement('script');

			el.src = conf.url;

			if (conf.failure) {
				el.onerror = function() {
					Wee.$exec(conf.failure, {
						args: conf.args,
						scope: conf.scope
					});
				}
			}

			head.appendChild(el);
		} else {
			var x = new XMLHttpRequest();

			x.onreadystatechange = function() {
				if (x.readyState === 4) {
					if (x.status >= 200 && x.status < 400) {
						if (conf.success) {
							var resp = x.responseText,
								orig = resp;

							// Parse JSON response if specified
							if (conf.json || conf.template) {
								try {
									resp = JSON.parse(resp);
								} catch (e) {
									resp = {};
								}

								if (conf.template) {
									resp = Wee.data.parse(conf.template, resp);
									conf.args.unshift(orig);
								}
							}

							conf.args.unshift(resp, x);

							// Execute success callback if specified
							Wee.$exec(conf.success, {
								args: conf.args,
								scope: conf.scope
							});

							return true;
						}
					} else {
						if (conf.failure) {
							conf.args.unshift(x);

							Wee.$exec(conf.failure, {
								args: conf.args,
								scope: conf.scope
							});
						}

						return false;
					}
				}
			};

			var send = null;

			// Post or get endpoint based on specification
			if (conf.method == 'post') {
				x.open('POST', conf.url, true);
				x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
				send = Wee.$isObject(conf.data) ? Wee.$serialize(conf.data) : conf.data;
			} else {
				if (Object.keys(conf.data).length > 0) {
					conf.url += '?' + Wee.$serialize(conf.data);
				}

				x.open('GET', conf.url, true);
			}

			// Set request headers
			if (conf.headers) {
				for (var key in conf.headers) {
					x.setRequestHeader(key, conf.headers[key]);
				}
			}

			// Send request
			x.send(send);
		}
	},
	// Parse specified data into specified template string
	// Return string
	parse: function(temp, data, opt) {
		return this.$private('render', temp, data, Wee.$extend({
			data: data,
			escape: true
		}, opt), 0);
	}
}, {
	pair: /{{([#^@]?)+(.+?)}}([\s\S]+?){{\/\1\2}}/g,
	single: /{{(.+?)}}/g,
	render: function(temp, data, conf, index) {
		var scope = this;

		return temp.replace(this.pair, function(m, pre, tag, inner) {
			var val = scope.getValue(data, tag, conf, index),
				empty = val === undefined || val.length === 0,
				resp = '';

			if (pre == '#' && ! empty && typeof val == 'object') {
				// Loop through objects and arrays
				if (typeof val == 'object') {
					var isObj = Wee.$isObject(val),
						i = 0;

					for (var key in val) {
						// Merge in iterative data
						var item = Wee.$extend({
							$key: key,
							$val: val[key],
							$i: i
						}, isObj ? val : val[key]);

						resp += scope.render(inner, item, conf, i);

						i++;
					}
				}
			} else if ((pre == '^' && empty) || (pre == '@' && ! empty)) {
				return scope.render(inner, data, conf);
			}

			return resp;
		}).replace(this.single, function(m, tag) {
			var opt = conf;

			// With % prefix output raw
			if (tag.charAt(0) == '%') {
				tag = tag.substring(1);
				opt = Wee.$extend({
					escape: false
				});
			}

			var resp = scope.getValue(data, tag, opt, index);

			return resp === undefined || typeof resp == 'object' ? '' : resp;
		});
	},
	getValue: function(data, key, conf, x) {
		var segs = key.split('||'),
			resp = segs[0].trim().split('.'),
			len = resp.length - 1,
			orig = data,
			i = 0;

		// Loop through object segments
		for (; i <= len; i++) {
			key = resp[i];
			data = data[key];

			// Return value on last segment
			if (i === len) {
				if (key.charAt(0) == '&') {
					data = conf.data[key.substring(1)];
				}

				if (typeof data == 'function') {
					data = data(orig, conf.data, x);
				}

				if (typeof data == 'string') {
					// Encode tags by default
					return conf.escape ? data
						.replace(/&/g, '&')
						.replace(/&/g, '&')
						.replace(/</g, '<')
						.replace(/>/g, '>')
						.replace(/"/g, '"') :
						data;
				} else if (data !== undefined) {
					return data;
				}
			}
		}

		// Return fallback or empty string
		return segs.length > 1 ? segs[1].trim() : '';
	}
});<|endoftext|>


<commit_before>var https = require( "https" ),
	userAgent = getUA();

function extend( a, b ) {
	for ( var p in b ) {
		a[ p ] = b[ p ];
	}

	return a;
}

function getUA() {
	var os = require( "os" ),
		version = require( "../package.json" ).version;
	return os.platform() + "/" + os.release() + " " +
		"node/" + process.versions.node + " " +
		"hookup/" + version;
}

function Client( settings ) {
	if ( !settings.username ) {
		throw new Error( "Username is required." );
	}
	if ( !settings.password ) {
		throw new Error( "Password is required." );
	}

	extend( this, settings );
	this.authHeader = "Basic " +
		new Buffer( this.username + ":" + this.password ).toString( "base64" );
}

Client.hookFields = [
	"name",
	"config",
	"events",
	"add_events",
	"remove_events",
	"active"
];

Client.events = {
	push: "Any git push to a Repository.",
	issues: "Any time an Issue is opened or closed.",
	issue_comment: "Any time an Issue is commented on.",
	commit_comment: "Any time a Commit is commented on.",
	pull_request: "Any time a Pull Request is opened, closed, or synchronized (updated due to a new push in the branch that the pull request is tracking).",
	pull_request_review_comment: "Any time a Commit is commented on while inside a Pull Request review (the Files Changed tab).",
	gollum: "Any time a Wiki page is updated.",
	watch: "Any time a User watches the Repository.",
	download: "Any time a Download is added to the Repository.",
	fork: "Any time a Repository is forked.",
	fork_apply: "Any time a patch is applied to the Repository from the Fork Queue.",
	member: "Any time a User is added as a collaborator to a non-Organization Repository.",
	"public": "Any time a Repository changes from private to public.",
	team_add: "Any time a team is added or modified on a Repository.",
	status: "Any time a Repository has a status update from the API."
};

Client.prototype.request = function( settings, data, callback ) {
	if ( typeof data === "function" ) {
		callback = data;
		data = null;
	} else {
		data = JSON.stringify( data );
	}
	callback = callback || function() {};

	var req = https.request( extend({
		host: "api.github.com",
		port: 443,
		method: "GET",
		headers: {
			authorization: this.authHeader,
			"user-agent": userAgent,
			"content-length": typeof data === "string" ? data.length : 0
		}
	}, settings ), function( res ) {
		var response = "";
		res.setEncoding( "utf8" );
		res.on( "data", function( chunk ) {
			response += chunk;
		});
		res.on( "end", function() {
			if ( res.statusCode >= 400 ) {
				var message;
				if ( res.headers[ "content-type" ].indexOf( "json" ) !== -1 ) {
					message = JSON.parse( response ).message;
				} else {
					message = response;
				}
				if ( !message && res.statusCode === 403 ) {
					message = "Forbidden";
				}
				callback( new Error( message ) );
			} else {
				callback( null, JSON.parse( response ) );
			}
		});
	});

	if ( data ) {
		req.write( data );
	}

	req.end();
};

// Provide a utility for getting all repos for an account so users can
// easily add a hook to all of their repos
Client.prototype.getRepos = function( user, callback ) {
	this.request({
		path: "/users/" + user + "/repos"
	}, callback );
};

Client.prototype.getHooks = function( repo, callback ) {
	this.request({
		path: "/repos/" + repo + "/hooks"
	}, callback );
};

Client.prototype.editHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.id ) {
		throw new Error( "Hook ID is required." );
	}

	var data = {};
	Client.hookFields.forEach(function( field ) {
		if ( settings.hasOwnProperty( field ) ) {
			data[ field ] = settings[ field ];
		}
	});

	this.request({
		path: "/repos/" + settings.repo + "/hooks/" + settings.id,
		method: "PATCH"
	}, data, callback );
};

Client.prototype.createHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.config ) {
		throw new Error( "Configuration is required." );
	}

	this.request({
		path: "/repos/" + settings.repo + "/hooks",
		method: "POST"
	}, settings, callback );
};

// Provide a simple abstraction for working with hooks by URL

Client.prototype.getHookByUrl = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.url ) {
		throw new Error( "URL is required." );
	}

	this.getHooks( settings.repo, function( error, hooks ) {
		if ( error ) {
			return callback( error );
		}

		var hook = hooks.filter(function( hook ) {
			return hook.config.url === settings.url;
		});

		callback( null, hook[ 0 ] );
	});
};

Client.prototype.addWebHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.events ) {
		throw new Error( "Events list is required." );
	}

	this.getHookByUrl( settings, function( error, hook ) {
		if ( error ) {
			return callback( error );
		}

		if ( hook ) {
			this.editHook({
				repo: settings.repo,
				id: hook.id,
				events: settings.events
			}, callback );
		} else {
			this.createHook({
				repo: settings.repo,
				name: "web",
				config: {
					url: settings.url,
					content_type: "form"
				},
				events: settings.events
			}, callback );
		}
	}.bind( this ) );
};

exports.Client = Client;
exports.createClient = function( settings ) {
	return new Client( settings );
};
<commit_msg>Updated list of events.<commit_after>var https = require( "https" ),
	userAgent = getUA();

function extend( a, b ) {
	for ( var p in b ) {
		a[ p ] = b[ p ];
	}

	return a;
}

function getUA() {
	var os = require( "os" ),
		version = require( "../package.json" ).version;
	return os.platform() + "/" + os.release() + " " +
		"node/" + process.versions.node + " " +
		"hookup/" + version;
}

function Client( settings ) {
	if ( !settings.username ) {
		throw new Error( "Username is required." );
	}
	if ( !settings.password ) {
		throw new Error( "Password is required." );
	}

	extend( this, settings );
	this.authHeader = "Basic " +
		new Buffer( this.username + ":" + this.password ).toString( "base64" );
}

Client.hookFields = [
	"name",
	"config",
	"events",
	"add_events",
	"remove_events",
	"active"
];

Client.events = {
	push: "Any git push to a Repository.",
	issues: "Any time an Issue is opened or closed.",
	issue_comment: "Any time an Issue is commented on.",
	commit_comment: "Any time a Commit is commented on.",
	create: "Any time a Repository, Branch, or Tag is created.",
	"delete": "Any time a Branch or Tag is deleted.",
	pull_request: "Any time a Pull Request is opened, closed, or synchronized (updated due to a new push in the branch that the pull request is tracking).",
	pull_request_review_comment: "Any time a Commit is commented on while inside a Pull Request review (the Files Changed tab).",
	gollum: "Any time a Wiki page is updated.",
	watch: "Any time a User watches the Repository.",
	release: "Any time a Release is published in the Repository.",
	fork: "Any time a Repository is forked.",
	fork_apply: "Any time a patch is applied to the Repository from the Fork Queue.",
	member: "Any time a User is added as a collaborator to a non-Organization Repository.",
	"public": "Any time a Repository changes from private to public.",
	team_add: "Any time a team is added or modified on a Repository.",
	status: "Any time a Repository has a status update from the API."
};

Client.prototype.request = function( settings, data, callback ) {
	if ( typeof data === "function" ) {
		callback = data;
		data = null;
	} else {
		data = JSON.stringify( data );
	}
	callback = callback || function() {};

	var req = https.request( extend({
		host: "api.github.com",
		port: 443,
		method: "GET",
		headers: {
			authorization: this.authHeader,
			"user-agent": userAgent,
			"content-length": typeof data === "string" ? data.length : 0
		}
	}, settings ), function( res ) {
		var response = "";
		res.setEncoding( "utf8" );
		res.on( "data", function( chunk ) {
			response += chunk;
		});
		res.on( "end", function() {
			if ( res.statusCode >= 400 ) {
				var message;
				if ( res.headers[ "content-type" ].indexOf( "json" ) !== -1 ) {
					message = JSON.parse( response ).message;
				} else {
					message = response;
				}
				if ( !message && res.statusCode === 403 ) {
					message = "Forbidden";
				}
				callback( new Error( message ) );
			} else {
				callback( null, JSON.parse( response ) );
			}
		});
	});

	if ( data ) {
		req.write( data );
	}

	req.end();
};

// Provide a utility for getting all repos for an account so users can
// easily add a hook to all of their repos
Client.prototype.getRepos = function( user, callback ) {
	this.request({
		path: "/users/" + user + "/repos"
	}, callback );
};

Client.prototype.getHooks = function( repo, callback ) {
	this.request({
		path: "/repos/" + repo + "/hooks"
	}, callback );
};

Client.prototype.editHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.id ) {
		throw new Error( "Hook ID is required." );
	}

	var data = {};
	Client.hookFields.forEach(function( field ) {
		if ( settings.hasOwnProperty( field ) ) {
			data[ field ] = settings[ field ];
		}
	});

	this.request({
		path: "/repos/" + settings.repo + "/hooks/" + settings.id,
		method: "PATCH"
	}, data, callback );
};

Client.prototype.createHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.config ) {
		throw new Error( "Configuration is required." );
	}

	this.request({
		path: "/repos/" + settings.repo + "/hooks",
		method: "POST"
	}, settings, callback );
};

// Provide a simple abstraction for working with hooks by URL

Client.prototype.getHookByUrl = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.url ) {
		throw new Error( "URL is required." );
	}

	this.getHooks( settings.repo, function( error, hooks ) {
		if ( error ) {
			return callback( error );
		}

		var hook = hooks.filter(function( hook ) {
			return hook.config.url === settings.url;
		});

		callback( null, hook[ 0 ] );
	});
};

Client.prototype.addWebHook = function( settings, callback ) {
	if ( !settings.repo ) {
		throw new Error( "Repository is required." );
	}
	if ( !settings.events ) {
		throw new Error( "Events list is required." );
	}

	this.getHookByUrl( settings, function( error, hook ) {
		if ( error ) {
			return callback( error );
		}

		if ( hook ) {
			this.editHook({
				repo: settings.repo,
				id: hook.id,
				events: settings.events
			}, callback );
		} else {
			this.createHook({
				repo: settings.repo,
				name: "web",
				config: {
					url: settings.url,
					content_type: "form"
				},
				events: settings.events
			}, callback );
		}
	}.bind( this ) );
};

exports.Client = Client;
exports.createClient = function( settings ) {
	return new Client( settings );
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-test-extra', 'test', function(Y) {

    var A = Y.Assert,
        AA = Y.ArrayAssert,
        OA = Y.ObjectAssert,
        cases,

        shared = require(Y.MOJITO_DIR + 'lib/app/commands/build/shared.js'),
        count;

    function getConf() {
        return {
            mojitodir: '/Users/isao/Repos/mojito/myfork/',
            app: {
                name: 'staticpf',
                version: '0.0.1',
                specs: {
                    frame: {},
                    tunnelProxy: {}
                },
                dir: '/path/to/app'
            },
            snapshot: {
                name: '',
                tag: '',
                packages: {}
            },
            build: {
                attachManifest: false,
                forceRelativePaths: false,
                insertCharset: 'UTF-8',
                port: 1111,
                dir: '/path/to/build/dir',
                type: 'html5app',
                uris: []
            },
            context: {
                device: 'iphone'
            },
            contextqs: '?device=iphone',
            tunnelpf: '/tunnel',
            staticpf: 'staticpf'
        };
    }

    cases = {
        name: 'build/shared cases',
        setUp: function() {
            count = 0;
            shared.init({
                copydir: function(file) { count++; return file; },
                copy: function(from, to) { count++; },
                write: function(file, str) { count++; },
            });
        },
        tearDown: function() {},

        'test init': function () {
            A.areSame(0, count);
            A.areSame(undefined, shared.init({}));
            A.areSame(0, count);
        },

        'test copyModule': function () {
            A.areSame(0, count);
            shared.copyModule('package/dir', 'module_name', 'dest/dir');
            A.areSame(1, count);
        },

        'test mapStoreUris modifies buildmap param by reference': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css'
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css'
                };

            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps css, js, json files': function () {
            var buildmap = {
                    '/?device=iphone': '/index.html'
                },
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/?device=iphone': '/index.html',
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps copies html, favicon, etc files to build dir': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            A.areSame(0, count);
            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
            A.areSame(2, count);
        },

        'test mapDefxUris adds definition.json for client mojits': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        url: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResourceVersions: function(filter) {return mojits;}
                },
                buildmap = {},
                expected = {'/tunnel/yahoo.application.test50/top_frame/definition.json?device=iphone': '/yahoo.application.test50/top_frame/definition.json'};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, getConf(), store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapDefxUris does nothing if mojit has no "url"': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        REDACTED_URL: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResourceVersions: function(filter) {return mojits;}
                },
                buildmap = {},
                expected = {};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, getConf(), store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris maps app.json specs to default.json files': function () {
            var buildmap = {},
                expected = {
                    '/tunnel/staticpf/frame/specs/default.json?device=iphone': '/staticpf/frame/specs/default.json',
                    '/tunnel/staticpf/tunnelProxy/specs/default.json?device=iphone': '/staticpf/tunnelProxy/specs/default.json'
                };

            A.areSame(0, count);
            shared.mapFunkySpecUris(buildmap, getConf());
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris handles obscure Livestand mojit specs': function () {
            var buildmap = {},
                expected = {'tunnelpf/staticpf/foo/specs/bar.json?foo=bar': '/staticpf/foo/specs/bar.json'};

            A.areSame(0, count);

            shared.mapFunkySpecUris(buildmap, {staticpf:'staticpf', contextqs: '?foo=bar', tunnelpf:'tunnelpf',app:{specs:{'foo:bar':{}}}});

            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test makeManifest writes a file': function () {
            var buildmap = buildmap = {
                    '/?device=iphone': '/index.html'
                };

            A.areSame(0, count);
            shared.makeManifest(buildmap, 'build/dir', new Date());
            A.areSame(1, count);
        },

        'test mungePage does nothing unless attachManifest, insertCharset, forceRelativePaths are true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr;

            A.areSame(0, count);
            newstr = shared.mungePage(getConf(), uri, oldstr);
            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage does nothing if uri is not *.html': function () {
            var uri = '/uri.css',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;
            conf.build.insertCharset = 'UTF-8';

            A.areSame(0, count);

            newstr = shared.mungePage(getConf(), uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage for attachManifest:true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for attachManifest:true can apply relative path': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for forceRelativePaths:true': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah  blah blah';
            expected = 'blah blah  blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);
        },

        'test mungePage for forceRelativePaths:true with no common root': function () {
            var uri = '/a/b/c/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah  blah blah';
            expected = 'blah blah  blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);
        },

        'test mungePage for insertCharset:true': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah \n<meta charset="UTF-8">\n blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for insertCharset:true simple tag': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah \n<meta charset="UTF-8">\n blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for insertCharset:true does nothing if there already is a charset metatag': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah<meta charset="zippy">',
                newstr,
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

    };

    Y.Test.Runner.add(new Y.Test.Case(cases));
});
<commit_msg>update test<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
YUI().use('mojito-test-extra', 'test', function(Y) {

    var A = Y.Assert,
        AA = Y.ArrayAssert,
        OA = Y.ObjectAssert,
        cases,

        shared = require(Y.MOJITO_DIR + 'lib/app/commands/build/shared.js'),
        count;

    function getConf() {
        return {
            mojitodir: '/Users/isao/Repos/mojito/myfork/',
            app: {
                name: 'staticpf',
                version: '0.0.1',
                specs: {
                    frame: {},
                    tunnelProxy: {}
                },
                dir: '/path/to/app'
            },
            snapshot: {
                name: '',
                tag: '',
                packages: {}
            },
            build: {
                attachManifest: false,
                forceRelativePaths: false,
                insertCharset: 'UTF-8',
                port: 1111,
                dir: '/path/to/build/dir',
                type: 'html5app',
                uris: []
            },
            context: {
                device: 'iphone'
            },
            contextqs: '?device=iphone',
            tunnelpf: '/tunnel',
            staticpf: 'staticpf'
        };
    }

    cases = {
        name: 'build/shared cases',
        setUp: function() {
            count = 0;
            shared.init({
                copydir: function(file) { count++; return file; },
                copy: function(from, to) { count++; },
                write: function(file, str) { count++; },
            });
        },
        tearDown: function() {},

        'test init': function () {
            A.areSame(0, count);
            A.areSame(undefined, shared.init({}));
            A.areSame(0, count);
        },

        'test copyModule': function () {
            A.areSame(0, count);
            shared.copyModule('package/dir', 'module_name', 'dest/dir');
            A.areSame(1, count);
        },

        'test mapStoreUris modifies buildmap param by reference': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css'
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css'
                };

            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps css, js, json files': function () {
            var buildmap = {
                    '/?device=iphone': '/index.html'
                },
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/?device=iphone': '/index.html',
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
        },

        'test mapStoreUris maps copies html, favicon, etc files to build dir': function () {
            var buildmap = {},
                storemap = {
                    '/staticpf/top_frame/assets/index.css': '/path/to/app/mojits/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js': '/path/to/app/mojits/top_frame/binders/index.js',
                    '/staticpf/top_frame/package.json': '/path/to/app/mojits/top_frame/package.json',
                    '/staticpf/top_frame/views/index.hb.html': '/path/to/app/mojits/top_frame/views/index.hb.html',
                    '/favicon.ico': '/Users/isao/Repos/mojito/myfork/lib/app/assets/favicon.ico',
                },
                expected = {
                    '/staticpf/top_frame/assets/index.css?device=iphone': '/staticpf/top_frame/assets/index.css',
                    '/staticpf/top_frameBinderIndex.js?device=iphone': '/staticpf/top_frameBinderIndex.js',
                    '/staticpf/top_frame/package.json?device=iphone': '/staticpf/top_frame/package.json'
                };

            A.areSame(0, count);
            shared.mapStoreUris(buildmap, getConf(), storemap);
            OA.areEqual(expected, buildmap);
            A.areSame(2, count);
        },

        'test mapDefxUris adds definition.json for client mojits': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        url: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResourceVersions: function(filter) {return mojits;}
                },
                buildmap = {},
                expected = {
                    '/tunnel/yahoo.application.test50/top_frame/definition.json?device=iphone': '/yahoo.application.test50/top_frame/definition.json',
                    '/tunnel/yahoo.application.test50/top_frame?device=iphone': '/yahoo.application.test50/top_frame'
                };

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, getConf(), store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapDefxUris does nothing if mojit has no "url"': function () {
            var mojits = [
                    {source: {
                        fs: {
                            fullPath: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootDir: '/Users/isao/Repos/mojito/apps/test50/test50/mojits/top_frame',
                            rootType: 'app',
                            subDir: '.',
                            subDirArray: [ '.' ],
                            isFile: false,
                            ext: '',
                            basename: ''
                        },
                        pkg: { name: 'yahoo.application.test50', version: '0.0.1', depth: 0 } },
                        type: 'mojit',
                        name: 'top_frame',
                        id: 'mojit--top_frame',
                        affinity: { affinity: 'common' },
                        selector: '*',
                        mime: { type: 'application/octet-stream', charset: undefined },
                        REDACTED_URL: '/yahoo.application.test50/top_frame'
                    }
                ],
                store = {
                    getResourceVersions: function(filter) {return mojits;}
                },
                buildmap = {},
                expected = {};

            A.areSame(0, count);
            shared.mapDefxUris(buildmap, getConf(), store);
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris maps app.json specs to default.json files': function () {
            var buildmap = {},
                expected = {
                    '/tunnel/staticpf/frame/specs/default.json?device=iphone': '/staticpf/frame/specs/default.json',
                    '/tunnel/staticpf/tunnelProxy/specs/default.json?device=iphone': '/staticpf/tunnelProxy/specs/default.json'
                };

            A.areSame(0, count);
            shared.mapFunkySpecUris(buildmap, getConf());
            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test mapFunkySpecUris handles obscure Livestand mojit specs': function () {
            var buildmap = {},
                expected = {'tunnelpf/staticpf/foo/specs/bar.json?foo=bar': '/staticpf/foo/specs/bar.json'};

            A.areSame(0, count);

            shared.mapFunkySpecUris(buildmap, {staticpf:'staticpf', contextqs: '?foo=bar', tunnelpf:'tunnelpf',app:{specs:{'foo:bar':{}}}});

            OA.areEqual(expected, buildmap);
            A.areSame(0, count);
        },

        'test makeManifest writes a file': function () {
            var buildmap = buildmap = {
                    '/?device=iphone': '/index.html'
                };

            A.areSame(0, count);
            shared.makeManifest(buildmap, 'build/dir', new Date());
            A.areSame(1, count);
        },

        'test mungePage does nothing unless attachManifest, insertCharset, forceRelativePaths are true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr;

            A.areSame(0, count);
            newstr = shared.mungePage(getConf(), uri, oldstr);
            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage does nothing if uri is not *.html': function () {
            var uri = '/uri.css',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;
            conf.build.insertCharset = 'UTF-8';

            A.areSame(0, count);

            newstr = shared.mungePage(getConf(), uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

        'test mungePage for attachManifest:true': function () {
            var uri = '/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for attachManifest:true can apply relative path': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for forceRelativePaths:true': function () {
            var uri = '/foo/bar/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah  blah blah';
            expected = 'blah blah  blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);
        },

        'test mungePage for forceRelativePaths:true with no common root': function () {
            var uri = '/a/b/c/uri.html',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah  blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);

            uri = '/foo/bar/baz/bah/uri.html';
            oldstr = 'blah blah  blah blah';
            expected = 'blah blah  blah blah';
            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(2, count);
        },

        'test mungePage for insertCharset:true': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah \n<meta charset="UTF-8">\n blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for insertCharset:true simple tag': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah',
                newstr,
                expected = 'blah blah \n<meta charset="UTF-8">\n blah blah',
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areNotSame(newstr, oldstr);
            A.areSame(expected, newstr);
            A.areSame(1, count);
        },

        'test mungePage for insertCharset:true does nothing if there already is a charset metatag': function () {
            var uri = '/',
                oldstr = 'blah blah  blah blah<meta charset="zippy">',
                newstr,
                conf = getConf();

            conf.build.attachManifest = true;
            conf.build.forceRelativePaths = true;

            A.areSame(0, count);

            newstr = shared.mungePage(conf, uri, oldstr);

            A.areSame(newstr, oldstr);
            A.areSame(1, count);
        },

    };

    Y.Test.Runner.add(new Y.Test.Case(cases));
});
<|endoftext|>