<commit_before>/**
 * @fileoverview Rule to check paired unsubscribe
 * @author Gvozd
 */
'use strict';

const defaultOptions = [
    {
        open: 'addEventListener',
        close: 'removeEventListener'
    },
    {
        open: 'on',
        close: 'off'
    },
    {
        open: 'subscribe',
        close: 'unsubscribe'
    }
];

module.exports = {
    meta: {
        docs: {
            description: 'check paired unsubscribe',
            category: 'Possible Errors',
            recommended: false
        },
        schema: [
            {
                type: 'array',
                items: {
                    type: 'object',
                    properties: {
                        open: {type: 'string'},
                        close: {type: 'string'},
                        contextEquality: {
                            type: 'boolean',
                            default: true
                        },
                        openArgumentsEquality: {
                            anyOf: [
                                {enum: [true]},
                                {
                                    type: 'array',
                                    items: {type: 'number'}
                                }
                            ],
                            default: true
                        },
                        closeArgumentsEquality: {
                            anyOf: [
                                {enum: [true]},
                                {
                                    type: 'array',
                                    items: {type: 'number'}
                                }
                            ],
                            default: true
                        }
                    },
                    required: ['open', 'close'],
                    additionalProperties: false
                }
            }
        ]
    },
    create(context) {
        const options = processOptions(context.options[0] || defaultOptions);
        const sourceCode = context.getSourceCode();
        const callExpressions = [];
        return {
            'CallExpression'(node) {
                const callee = node.callee;
                const callArgs = node.arguments;
                let methodName;
                switch (callee.type) {
                    case 'MemberExpression':
                        methodName = callee.property.name;
                        break;
                    case 'Identifier':
                        methodName = callee.name;
                        break;
                    case 'FunctionExpression':
                    case 'CallExpression':
                        break;
                    default:
                        throw new Error('unknown callee type: ' + callee.type);
                }
                callExpressions.push({
                    node,
                    methodName,
                    contextSource: sourceCode.getTokens(callee)
                        .map((x) => x.value).join(''),
                    argumentsSources: callArgs.map(
                        (arg) => sourceCode.getTokens(arg)
                            .map((x) => x.value).join('')
                    )
                });
            },
            'Program:exit'() {
                for (const callExpression of callExpressions) {
                    if (!options.hasOwnProperty(callExpression.methodName)) {
                        continue;
                    }
                    if (!hasPair(callExpression, callExpressions, options)) {
                        context.report({
                            node: callExpression.node,
                            message: `unpaired "${callExpression.methodName}"`
                        });
                    }
                }
            }
        };
    }
};

function processOptions(options) {
    const pairs = {};
    for (const option of options) {
        pairs[option.open] = {
            open: option.open,
            close: option.close,
            contextEquality: 'contextEquality' in option ? option.contextEquality : true,
            openArgumentsEquality: ('openArgumentsEquality' in option) ? option.openArgumentsEquality : true,
            closeArgumentsEquality: ('closeArgumentsEquality' in option) ? option.closeArgumentsEquality : true
        };
    }
    return pairs;
}

function hasPair(openCall, callExpressions, options) {
    const option = options[openCall.methodName];
    const closeName = option.close;
    return callExpressions.some(isCloseCall);

    function isCloseCall(closeCall) {
        if (closeCall.methodName !== closeName) {
            return false;
        }
        if (option.contextEquality) {
            if (closeCall.contextSource.split(closeName).join(openCall.methodName) !== openCall.contextSource) {
                return false;
            }
        }
        const openArgs = sliceByMask(openCall.argumentsSources, option.openArgumentsEquality);
        const closeArgs = sliceByMask(closeCall.argumentsSources, option.closeArgumentsEquality);
        return closeArgs.join() === openArgs.join();
    }
}

function sliceByMask(array, mask) {
    if (mask === true) {
        return array;
    }
    return mask.map((idx) => {
        if (idx < 0) {
            idx = array.length + idx;
        }
        return array[idx];
    });
}
<commit_msg>Do not crash on arrow functions<commit_after>/**
 * @fileoverview Rule to check paired unsubscribe
 * @author Gvozd
 */
'use strict';

const defaultOptions = [
    {
        open: 'addEventListener',
        close: 'removeEventListener'
    },
    {
        open: 'on',
        close: 'off'
    },
    {
        open: 'subscribe',
        close: 'unsubscribe'
    }
];

module.exports = {
    meta: {
        docs: {
            description: 'check paired unsubscribe',
            category: 'Possible Errors',
            recommended: false
        },
        schema: [
            {
                type: 'array',
                items: {
                    type: 'object',
                    properties: {
                        open: {type: 'string'},
                        close: {type: 'string'},
                        contextEquality: {
                            type: 'boolean',
                            default: true
                        },
                        openArgumentsEquality: {
                            anyOf: [
                                {enum: [true]},
                                {
                                    type: 'array',
                                    items: {type: 'number'}
                                }
                            ],
                            default: true
                        },
                        closeArgumentsEquality: {
                            anyOf: [
                                {enum: [true]},
                                {
                                    type: 'array',
                                    items: {type: 'number'}
                                }
                            ],
                            default: true
                        }
                    },
                    required: ['open', 'close'],
                    additionalProperties: false
                }
            }
        ]
    },
    create(context) {
        const options = processOptions(context.options[0] || defaultOptions);
        const sourceCode = context.getSourceCode();
        const callExpressions = [];
        return {
            'CallExpression'(node) {
                const callee = node.callee;
                const callArgs = node.arguments;
                let methodName;
                switch (callee.type) {
                    case 'MemberExpression':
                        methodName = callee.property.name;
                        break;
                    case 'Identifier':
                        methodName = callee.name;
                        break;
                    case 'FunctionExpression':
                    case 'CallExpression':
                    case 'ArrowFunctionExpression':
                        break;
                    default:
                        throw new Error('unknown callee type: ' + callee.type);
                }
                callExpressions.push({
                    node,
                    methodName,
                    contextSource: sourceCode.getTokens(callee)
                        .map((x) => x.value).join(''),
                    argumentsSources: callArgs.map(
                        (arg) => sourceCode.getTokens(arg)
                            .map((x) => x.value).join('')
                    )
                });
            },
            'Program:exit'() {
                for (const callExpression of callExpressions) {
                    if (!options.hasOwnProperty(callExpression.methodName)) {
                        continue;
                    }
                    if (!hasPair(callExpression, callExpressions, options)) {
                        context.report({
                            node: callExpression.node,
                            message: `unpaired "${callExpression.methodName}"`
                        });
                    }
                }
            }
        };
    }
};

function processOptions(options) {
    const pairs = {};
    for (const option of options) {
        pairs[option.open] = {
            open: option.open,
            close: option.close,
            contextEquality: 'contextEquality' in option ? option.contextEquality : true,
            openArgumentsEquality: ('openArgumentsEquality' in option) ? option.openArgumentsEquality : true,
            closeArgumentsEquality: ('closeArgumentsEquality' in option) ? option.closeArgumentsEquality : true
        };
    }
    return pairs;
}

function hasPair(openCall, callExpressions, options) {
    const option = options[openCall.methodName];
    const closeName = option.close;
    return callExpressions.some(isCloseCall);

    function isCloseCall(closeCall) {
        if (closeCall.methodName !== closeName) {
            return false;
        }
        if (option.contextEquality) {
            if (closeCall.contextSource.split(closeName).join(openCall.methodName) !== openCall.contextSource) {
                return false;
            }
        }
        const openArgs = sliceByMask(openCall.argumentsSources, option.openArgumentsEquality);
        const closeArgs = sliceByMask(closeCall.argumentsSources, option.closeArgumentsEquality);
        return closeArgs.join() === openArgs.join();
    }
}

function sliceByMask(array, mask) {
    if (mask === true) {
        return array;
    }
    return mask.map((idx) => {
        if (idx < 0) {
            idx = array.length + idx;
        }
        return array[idx];
    });
}
<|endoftext|>


<commit_before>'use strict';

let yamljs = require('../lib/yamljs/Yaml');
let restler = require('restler');

let Connection = require(appRoot + '/src/common/models/Connection');
let Content = require(appRoot + '/src/common/models/Content');

class GitHubConnection extends Connection {
    constructor(data) {
        super(data);
    }

    /**
     * Compiles content for Jekyll
     *
     * @param {Object} properties
     *
     * @returns {Promise} promise
     */
    compileForJekyll(properties) {
        debug.log('Compiling "' + properties.title + '" for Jekyll...', this);

        let frontMatter = '';

        frontMatter += '---\n';
        frontMatter += yamljs.stringify(properties);
        frontMatter += '\n---';

        return frontMatter;
    }

    /**
     * Posts content properties to the remote target
     *
     * @param {Object} properties
     *
     * @returns {Promise} promise
     */
    postContentProperties(properties) {
        return new Promise((resolve, reject) => {
            let path = properties.url || ContentHelper.getSlug(properties.title);

            // Remove first and last slash
            if(path[0] == '/') {
                path = path.substring(1);
            }

            if(path[path.length - 1] == '/') {
                path = path.substring(0, path.length - 1);
            }

            // Add the markdown extension
            path += '.md';

            let apiPath = 'https://api.github.com/repos/' + this.settings.repo + '/contents/' + path + '?access_token=' + this.settings.token;
            let fileContent = this.compileForJekyll(properties);
            let fileObject = JSON.stringify({ content: fileContent });
            let headers = {
                'Accept': 'application/json'
            };

            debug.log('Uploading "' + path + '"...', this);

            // Fetch first to get the SHA
            debug.log('Getting SHA...', this);

            restler.get(apiPath, {
                headers: headers
            }).on('complete', (data, response) => {
                let postData = {
                    sha: data.sha,
                    path: path,
                    message: 'Commit from Endomon CMS',
                    content: new Buffer(fileObject).toString('base64')
                };

                // Commit the file
                debug.log('Committing data...', this);

                restler.put(apiPath, {
                    headers: headers,
                    data: postData
                }).on('complete', (data, response) => {
                    if(data.message) {
                        debug.log('GitHub response: ' + JSON.stringify(data), this);
                        debug.log('URL: ' + apiPath, this);
                        debug.log('Data: ' + JSON.stringify(postData), this);

                    } else {
                        debug.log('Committed file successfully!', this);

                    }

                    resolve();

                });
            });
        });
    }
}

module.exports = GitHubConnection;
<commit_msg>getting through to github<commit_after>'use strict';

let yamljs = require('../lib/yamljs/Yaml');
let restler = require('restler');

let Connection = require(appRoot + '/src/common/models/Connection');
let Content = require(appRoot + '/src/common/models/Content');

class GitHubConnection extends Connection {
    constructor(data) {
        super(data);
    }

    /**
     * Compiles content for Jekyll
     *
     * @param {Object} properties
     *
     * @returns {Promise} promise
     */
    compileForJekyll(properties) {
        debug.log('Compiling "' + properties.title + '" for Jekyll...', this);

        let frontMatter = '';

        frontMatter += '---\n';
        frontMatter += yamljs.stringify(properties);
        frontMatter += '\n---';

        return frontMatter;
    }

    /**
     * Posts content properties to the remote target
     *
     * @param {Object} properties
     *
     * @returns {Promise} promise
     */
    postContentProperties(properties) {
        return new Promise((resolve, reject) => {
            let path = properties.url || ContentHelper.getSlug(properties.title);

            // Remove first and last slash
            if(path[0] == '/') {
                path = path.substring(1);
            }

            if(path[path.length - 1] == '/') {
                path = path.substring(0, path.length - 1);
            }

            // Add the markdown extension
            path += '.md';

            // Add the root directory
            path = this.settings.content + '/' + path;

            let apiPath = 'https://api.github.com/repos/' + this.settings.repo + '/contents/' + path + '?access_token=' + this.settings.token;
            let fileContent = this.compileForJekyll(properties);
            let headers = {
                'Accept': 'application/json'
            };

            debug.log('Uploading "' + path + '"...', this);

            // Fetch first to get the SHA
            debug.log('Getting SHA...', this);

            restler.get(apiPath, {
                headers: headers
            }).on('complete', (data, response) => {
                let postData = {
                    sha: data.sha,
                    path: path,
                    message: 'Commit from Endomon CMS',
                    content: new Buffer(fileContent).toString('base64')
                };

                // Commit the file
                debug.log('Committing data...', this);

                restler.put(apiPath, {
                    headers: headers,
                    data: JSON.stringify(postData)
                }).on('complete', (data, response) => {
                    if(data.message) {
                        debug.log('Committing file failed', this);
                        debug.log('GitHub response: ' + JSON.stringify(data), this);

                    } else {
                        debug.log('Committed file successfully!', this);

                    }

                    resolve();

                });
            });
        });
    }
}

module.exports = GitHubConnection;
<|endoftext|>


<commit_before>module('routing', {
    afterEach: function() {
        App.reset()
    },
})

test('document title is created correctly', function(assert) {
    var handler = Ember.Object.createWithMixins(App.TitleHandler)
    assert.equal(handler._titleFactory('test'), 'test - Shepherd')
    assert.equal(handler._titleFactory(), 'Shepherd')
})

test('document title on initial page load is set correctly', function(assert) {
    document.title = 'test'
    visit('/')
    andThen(function() {
        assert.equal(document.title, 'Readme - Shepherd')
    })
})
<commit_msg>title tests typo fix<commit_after>module('title', {
    afterEach: function() {
        App.reset()
    },
})

test('document title is created correctly', function(assert) {
    var handler = Ember.Object.createWithMixins(App.TitleHandler)
    assert.equal(handler._titleFactory('test'), 'test - Shepherd')
    assert.equal(handler._titleFactory(), 'Shepherd')
})

test('document title on initial page load is set correctly', function(assert) {
    document.title = 'test'
    visit('/')
    andThen(function() {
        assert.equal(document.title, 'Readme - Shepherd')
    })
})
<|endoftext|>


<commit_before>import Link from 'next/link'

const MainLink = ({ href, as, children }) => (

    <Link href={href} as={as}>
      {children}
    </Link>

)

export default () => (

Denis Weerasiri

        <MainLink href="https://twitter.com/ddweerasiri">Twitter</MainLink>

        <MainLink href="https://github.com/ddweerasiri">GitHub</MainLink>

We're under construction. Please check back for an update soon.

)
<commit_msg>Commenting out the blog page link from home page<commit_after>import Link from 'next/link'

const MainLink = ({ href, as, children }) => (

    <Link href={href} as={as}>
      {children}
    </Link>

)

export default () => (

Denis Weerasiri

        <MainLink href="https://twitter.com/ddweerasiri">Twitter</MainLink>
        {/*<MainLink href="/blog">Blog</MainLink>*/}
        <MainLink href="https://github.com/ddweerasiri">GitHub</MainLink>

We're under construction. Please check back for an update soon.

)
<|endoftext|>


<commit_before>import { NextSeo } from 'next-seo';
import Link from 'next/link';
import React from 'react';

import Banner from '../components/Banner';
import Carousel from '../components/Carousel';
import Committees from '../components/CommitteeSpread';
import ContentBanner from '../components/ContentBanner';
import Layout from '../components/Layout';
import Article from '../components/NewsArticle';
import SocialMedia from '../components/SocialMedia';
import data from '../data';

function Home () {
	const { carousel, committees, news } = data;
	return (
		<Layout>
			<NextSeo
				title="Home | ACM at UCLA"
				description="The ACM Student Chapter at UCLA is UCLA's largest tech community, focused on making tech as accessible as possible. We're split up into an array of committees and initiatives that each focus on a specific area of computer science. Everyone is welcome to join - regardless of major, prior experience, or anything else!"
				openGraph={{
					images: [
						{
							url: 'https://www.uclaacm.com/images/logo.png',
							width: 1200,
							height: 1200,
							alt: 'The ACM at UCLA logo',
						},
					],
					site_name: 'ACM at UCLA',
				}}
			/>

				<ContentBanner
					title="ACM Spring Officer Applications are out!"
					ctaText="Apply"
					ctaLink="https://tinyurl.com/acmspringofficer"
				/>
				<Banner />

The largest Computer Science community at UCLA

The ACM Student Chapter at UCLA is the largest computer science student organization in Southern California. We welcome students of all backgrounds, interests, and skill levels to join our community and share our love for technology. Our chapter is comprised of eight committees — each serving a different topic and mission. Our events are open to everyone, regardless of major or experience!

					<SocialMedia />

				<Committees committees={committees} />

					<Link href="/committees">Learn More About Our Committees</Link>

In the News

						{news.map(
							article =>
,
						)}

Want to stay updated on what’s going on?

							<Link href="http://eepurl.com/c5pE6P">

									Join our Mailing List

							</Link>

				<Carousel images={carousel.images} aria-hidden="true"/>

		</Layout>
	);
}

export default Home;
<commit_msg>changed the banner to town hall (#495)<commit_after>import { NextSeo } from 'next-seo';
import Link from 'next/link';
import React from 'react';

import Banner from '../components/Banner';
import Carousel from '../components/Carousel';
import Committees from '../components/CommitteeSpread';
import ContentBanner from '../components/ContentBanner';
import Layout from '../components/Layout';
import Article from '../components/NewsArticle';
import SocialMedia from '../components/SocialMedia';
import data from '../data';

function Home () {
	const { carousel, committees, news } = data;
	return (
		<Layout>
			<NextSeo
				title="Home | ACM at UCLA"
				description="The ACM Student Chapter at UCLA is UCLA's largest tech community, focused on making tech as accessible as possible. We're split up into an array of committees and initiatives that each focus on a specific area of computer science. Everyone is welcome to join - regardless of major, prior experience, or anything else!"
				openGraph={{
					images: [
						{
							url: 'https://www.uclaacm.com/images/logo.png',
							width: 1200,
							height: 1200,
							alt: 'The ACM at UCLA logo',
						},
					],
					site_name: 'ACM at UCLA',
				}}
			/>

				<ContentBanner
					title="Fill Out The Survey and RSVP For the CS Spring Town Hall!"
					ctaText="Learn More"
					ctaLink="/town-hall/s22"
				/>
				<Banner />

The largest Computer Science community at UCLA

The ACM Student Chapter at UCLA is the largest computer science student organization in Southern California. We welcome students of all backgrounds, interests, and skill levels to join our community and share our love for technology. Our chapter is comprised of eight committees — each serving a different topic and mission. Our events are open to everyone, regardless of major or experience!

					<SocialMedia />

				<Committees committees={committees} />

					<Link href="/committees">Learn More About Our Committees</Link>

In the News

						{news.map(
							article =>
,
						)}

Want to stay updated on what’s going on?

							<Link href="http://eepurl.com/c5pE6P">

									Join our Mailing List

							</Link>

				<Carousel images={carousel.images} aria-hidden="true"/>

		</Layout>
	);
}

export default Home;
<|endoftext|>


<commit_before>import React from 'react'
import {Link} from 'react-router'
import {prefixLink} from 'gatsby-helpers'
import Helmet from 'react-helmet'
import {config} from 'config'
import ExecutionEnvironment from 'exenv'; // Environment checking for universal apps
import ReactCursorPosition from 'react-cursor-position';

// ** REACT THREE ** //
import React3 from 'react-three-renderer';
import * as THREE from 'three';
import ReactDOM from 'react-dom';
// ** REACT THREE ** //

class Simple extends React.Component {
    constructor(props, context) {
        super(props, context);

        // construct the position vector here, because if we use 'new' within render,
        // React will think that things have changed when they have not.

        this.renderClock = new THREE.Clock(true);

        this.cameraPosition = new THREE.Vector3(0, 0, 50);

        var textureLoader = new THREE.TextureLoader();
        const sprite1 = textureLoader.load("./snowflake.png");
        const sprite2 = textureLoader.load("./snowflake-2.png");
        const sprite3 = textureLoader.load("./snowflake-3.png");
        const sprite4 = textureLoader.load("./snowflake-4.png");
        const sprite5 = textureLoader.load("./snowflake-5.png");

        this.particleCount = 200;
        this.particleParameters = [
            [
                [
                    1.0, 0.2, 0.5
                ],
                sprite2,
                3
            ],
            [
                [
                    0.95, 0.1, 0.5
                ],
                sprite3,
                15
            ],
            [
                [
                    0.90, 0.05, 0.5
                ],
                sprite1,
                7
            ],
            [
                [
                    0.85, 0, 0.5
                ],
                sprite5,
                8
            ],
            [
                [
                    0.80, 0, 0.5
                ],
                sprite4,
                5
            ]
        ];

        this.state = {
            cubeRotation: new THREE.Euler(),
            particleVertices: null,
            particleRotations: null,
        };

        this._onAnimate = () => {
            // we will get this callback every frame

            // pretend cubeRotation is immutable.
            // this helps with updates and pure rendering.
            // React will be sure that the rotation has now updated.
            // this.animateParticles()

            if (this.state.particleVertices != null) {
              this.updateParticles(() => {
                this.particleSystem = this.createParticleSystem();
              })
            }

            this.setState({
                cubeRotation: new THREE.Euler(this.state.cubeRotation.x + 0.1, this.state.cubeRotation.y + 0.1, 0)
            });
        };
    }

    animateParticles() {
        for (let i = 0; i < this.state.particleSystem.length; i++) {

            let particleBlock = this.particleSystem[i];

            var verts = particleBlock.geometry.vertices;
            for (let ii = 0; ii < verts.length; ii++) {
                var vert = verts[ii];
                if (vert.z < -200) {
                    vert.z = Math.random() * 400 - 200;
                }
                vert.z = vert.z + (0.2 * deltaTime);
                // vert.y = vert.y - (lastScrollY * 0.002);
            }
            particleBlock.geometry.verticesNeedUpdate = true;

            particleBlock.rotation.y -= .005 * deltaTime;
            particleBlock.position.y = lastScrollY * 0.2 * parallaxVals[i];
            console.log('particle block: ' + i);
            particleBlock.rotation.x = .02 * mouseY * 0.01 * parallaxVals[i];
            particleBlock.rotation.y = .01 * mouseX * 0.01 * parallaxVals[i];

        }
    }

    updateParticles(callback) {

      const deltaTime = this.renderClock.getDelta();
      // Vertices
      const newVertices = this.state.particleVertices.map((vertex) => {
        vertex.z += 0.3 * deltaTime * (window.innerWidth*0.5/2 - this.props.cursorPosition.x);
        return vertex;
      })

      // Block rotation
      const newRotations = this.state.particleRotations.map((rotation) => {
        rotation.z -= 0.5 * deltaTime;
        return rotation
      })

      this.setState({
        particleVertices: newVertices,
        particleRotations: newRotations,
      }, callback())
    }

    createParticleSystem() {
        var tempParticleArray = [];

        let geometryVertices = this.state.particleVertices;

        if (geometryVertices == null) {

          geometryVertices = [];

          for (let i = 0; i < this.particleCount; i++) {
              var vertex = new THREE.Vector3();
              vertex.x = (2*Math.random() - 1) * this.particleCount;
              vertex.y = (2*Math.random() - 1) * this.particleCount;
              vertex.z = (2*Math.random() - 1) * this.particleCount;
              geometryVertices.push(vertex);
          }
        }

        let particleRotations = this.state.particleRotations;
        if (particleRotations == null) {
          particleRotations = [];
        }

        for (const particleParameter of this.particleParameters) {
            const particleColor = particleParameter[0];
            const particleSprite = particleParameter[1];
            const particleSize = particleParameter[2];

            const rotationIndex = this.particleParameters.indexOf(particleParameter);
            let particleRotation = particleRotations[rotationIndex];
            if (particleRotation == null) {
              particleRotation = new THREE.Euler(Math.random() * 6, Math.random() * 6, Math.random() * 6);
              particleRotations.push(particleRotation);
            }

            const particleFog = new THREE.FogExp2(0x000000, 0.0008);

            const particles = (
                <points rotation={particleRotation}>
                  <geometry vertices={geometryVertices} // USE STATE OR OTHER
                    dynamic={true}/>
                  <pointsMaterial size={particleSize} map={particleSprite} blending={THREE.AdditiveBlending} depthTest={false} transparent={true} color={particleColor} fog={particleFog}/>
                </points>
            )
            tempParticleArray.push(particles);
        }

        this.setState({
          particleVertices: geometryVertices,
          particleRotations: particleRotations,
        });
        return tempParticleArray;

    }

    componentWillMount() {
      this.particleSystem = this.createParticleSystem();
    }

    render() {

        const width = window.innerWidth*0.5; // canvas width
        const height = window.innerHeight; // canvas height

        const particles = this.particleSystem.map((particle) => {
            return particle
        })

        return (
            <React3 mainCamera="camera" // this points to the perspectiveCamera which has the name set to "camera" below
              width={width} height={height} onAnimate={this._onAnimate}>
              <scene>
                <perspectiveCamera name="camera" fov={70} aspect={width / height} near={1} far={1000} position={this.cameraPosition}/>
                <directionalLight position={new THREE.Vector3(1, -1, 1)}/>
                {particles}
                <mesh rotation={this.state.cubeRotation}>
                  <boxGeometry width={1} height={1} depth={1}/>
                  <meshBasicMaterial color={0x00ff00}/>
                </mesh>
              </scene>
            </React3>
        );
    }
}

export default class Index extends React.Component {

    constructor(props) {
      super(props)
      this.state = {
        canUseDOM: ExecutionEnvironment.canUseDOM,
      }
    }

    render() {

      const canUseDOM = this.state.canUseDOM;
      let canvas = null

      if (canUseDOM) {
        canvas = (
        <ReactCursorPosition>
          <Simple/>
        </ReactCursorPosition>
      )
      }

        return (

              <Helmet title={config.siteTitle} meta={[
                {
                        "name": "description",
                        "content": "Sample"
                }, {
                        "name": "keywords",
                        "content": "sample, something"
                }
              ]}/>
              {canvas}

                  Hi people

Welcome to your new Gatsby site

Below are some pages showing different capabilities built-in to Gatsby

Supported file types

                    <Link to={prefixLink('/markdown/')}>Markdown</Link>

                    <Link to={prefixLink('/react/')}>JSX (React components)</Link>

                    <Link to={prefixLink('/coffee-react/')}>CJSX (Coffeescript React components)</Link>

                    <Link to={prefixLink('/html/')}>HTML</Link>

                    <Link to={prefixLink('/json/')}>JSON</Link>

                    <Link to={prefixLink('/yaml/')}>YAML</Link>

                    <Link to={prefixLink('/toml/')}>TOML</Link>

Supported CSS processors

                    <Link to={prefixLink('/postcss/')}>PostCSS</Link>

                    <Link to={prefixLink('/css-modules/')}>CSS Modules</Link>

                    <Link to={prefixLink('/sass/')}>Sass</Link>

                    <Link to={prefixLink('/less/')}>Less</Link>

                )
                }
                }
<commit_msg>better canvas size for testing for now<commit_after>import React from 'react'
import {Link} from 'react-router'
import {prefixLink} from 'gatsby-helpers'
import Helmet from 'react-helmet'
import {config} from 'config'
import ExecutionEnvironment from 'exenv'; // Environment checking for universal apps
import ReactCursorPosition from 'react-cursor-position';

// ** REACT THREE ** //
import React3 from 'react-three-renderer';
import * as THREE from 'three';
import ReactDOM from 'react-dom';
// ** REACT THREE ** //

class Simple extends React.Component {
    constructor(props, context) {
        super(props, context);

        // construct the position vector here, because if we use 'new' within render,
        // React will think that things have changed when they have not.

        this.renderClock = new THREE.Clock(true);

        this.cameraPosition = new THREE.Vector3(0, 0, 50);

        var textureLoader = new THREE.TextureLoader();
        const sprite1 = textureLoader.load("./snowflake.png");
        const sprite2 = textureLoader.load("./snowflake-2.png");
        const sprite3 = textureLoader.load("./snowflake-3.png");
        const sprite4 = textureLoader.load("./snowflake-4.png");
        const sprite5 = textureLoader.load("./snowflake-5.png");

        this.particleCount = 200;
        this.particleParameters = [
            [
                [
                    1.0, 0.2, 0.5
                ],
                sprite2,
                3
            ],
            [
                [
                    0.95, 0.1, 0.5
                ],
                sprite3,
                15
            ],
            [
                [
                    0.90, 0.05, 0.5
                ],
                sprite1,
                7
            ],
            [
                [
                    0.85, 0, 0.5
                ],
                sprite5,
                8
            ],
            [
                [
                    0.80, 0, 0.5
                ],
                sprite4,
                5
            ]
        ];

        this.state = {
            cubeRotation: new THREE.Euler(),
            particleVertices: null,
            particleRotations: null,
        };

        this._onAnimate = () => {
            // we will get this callback every frame

            // pretend cubeRotation is immutable.
            // this helps with updates and pure rendering.
            // React will be sure that the rotation has now updated.
            // this.animateParticles()

            if (this.state.particleVertices != null) {
              this.updateParticles(() => {
                this.particleSystem = this.createParticleSystem();
              })
            }

            this.setState({
                cubeRotation: new THREE.Euler(this.state.cubeRotation.x + 0.1, this.state.cubeRotation.y + 0.1, 0)
            });
        };
    }

    animateParticles() {
        for (let i = 0; i < this.state.particleSystem.length; i++) {

            let particleBlock = this.particleSystem[i];

            var verts = particleBlock.geometry.vertices;
            for (let ii = 0; ii < verts.length; ii++) {
                var vert = verts[ii];
                if (vert.z < -200) {
                    vert.z = Math.random() * 400 - 200;
                }
                vert.z = vert.z + (0.2 * deltaTime);
                // vert.y = vert.y - (lastScrollY * 0.002);
            }
            particleBlock.geometry.verticesNeedUpdate = true;

            particleBlock.rotation.y -= .005 * deltaTime;
            particleBlock.position.y = lastScrollY * 0.2 * parallaxVals[i];
            console.log('particle block: ' + i);
            particleBlock.rotation.x = .02 * mouseY * 0.01 * parallaxVals[i];
            particleBlock.rotation.y = .01 * mouseX * 0.01 * parallaxVals[i];

        }
    }

    updateParticles(callback) {

      const deltaTime = this.renderClock.getDelta();
      // Vertices
      const newVertices = this.state.particleVertices.map((vertex) => {
        vertex.z += 0.3 * deltaTime * (window.innerWidth*0.75/2 - this.props.cursorPosition.x);
        return vertex;
      })

      // Block rotation
      const newRotations = this.state.particleRotations.map((rotation) => {
        rotation.z -= 0.5 * deltaTime;
        return rotation
      })

      this.setState({
        particleVertices: newVertices,
        particleRotations: newRotations,
      }, callback())
    }

    createParticleSystem() {
        var tempParticleArray = [];

        let geometryVertices = this.state.particleVertices;

        if (geometryVertices == null) {

          geometryVertices = [];

          for (let i = 0; i < this.particleCount; i++) {
              var vertex = new THREE.Vector3();
              vertex.x = (2*Math.random() - 1) * this.particleCount;
              vertex.y = (2*Math.random() - 1) * this.particleCount;
              vertex.z = (2*Math.random() - 1) * this.particleCount;
              geometryVertices.push(vertex);
          }
        }

        let particleRotations = this.state.particleRotations;
        if (particleRotations == null) {
          particleRotations = [];
        }

        for (const particleParameter of this.particleParameters) {
            const particleColor = particleParameter[0];
            const particleSprite = particleParameter[1];
            const particleSize = particleParameter[2];

            const rotationIndex = this.particleParameters.indexOf(particleParameter);
            let particleRotation = particleRotations[rotationIndex];
            if (particleRotation == null) {
              particleRotation = new THREE.Euler(Math.random() * 6, Math.random() * 6, Math.random() * 6);
              particleRotations.push(particleRotation);
            }

            const particleFog = new THREE.FogExp2(0x000000, 0.0008);

            const particles = (
                <points rotation={particleRotation}>
                  <geometry vertices={geometryVertices} // USE STATE OR OTHER
                    dynamic={true}/>
                  <pointsMaterial size={particleSize} map={particleSprite} blending={THREE.AdditiveBlending} depthTest={false} transparent={true} color={particleColor} fog={particleFog}/>
                </points>
            )
            tempParticleArray.push(particles);
        }

        this.setState({
          particleVertices: geometryVertices,
          particleRotations: particleRotations,
        });
        return tempParticleArray;

    }

    componentWillMount() {
      this.particleSystem = this.createParticleSystem();
    }

    render() {

        const width = window.innerWidth*0.75; // canvas width
        const height = window.innerHeight; // canvas height

        const particles = this.particleSystem.map((particle) => {
            return particle
        })

        return (
            <React3 mainCamera="camera" // this points to the perspectiveCamera which has the name set to "camera" below
              width={width} height={height} onAnimate={this._onAnimate}>
              <scene>
                <perspectiveCamera name="camera" fov={70} aspect={width / height} near={1} far={1000} position={this.cameraPosition}/>
                <directionalLight position={new THREE.Vector3(1, -1, 1)}/>
                {particles}
                <mesh rotation={this.state.cubeRotation}>
                  <boxGeometry width={1} height={1} depth={1}/>
                  <meshBasicMaterial color={0x00ff00}/>
                </mesh>
              </scene>
            </React3>
        );
    }
}

export default class Index extends React.Component {

    constructor(props) {
      super(props)
      this.state = {
        canUseDOM: ExecutionEnvironment.canUseDOM,
      }
    }

    render() {

      const canUseDOM = this.state.canUseDOM;
      let canvas = null

      if (canUseDOM) {
        canvas = (
        <ReactCursorPosition>
          <Simple/>
        </ReactCursorPosition>
      )
      }

        return (

              <Helmet title={config.siteTitle} meta={[
                {
                        "name": "description",
                        "content": "Sample"
                }, {
                        "name": "keywords",
                        "content": "sample, something"
                }
              ]}/>
              {canvas}

                  Hi people

Welcome to your new Gatsby site

Below are some pages showing different capabilities built-in to Gatsby

Supported file types

                    <Link to={prefixLink('/markdown/')}>Markdown</Link>

                    <Link to={prefixLink('/react/')}>JSX (React components)</Link>

                    <Link to={prefixLink('/coffee-react/')}>CJSX (Coffeescript React components)</Link>

                    <Link to={prefixLink('/html/')}>HTML</Link>

                    <Link to={prefixLink('/json/')}>JSON</Link>

                    <Link to={prefixLink('/yaml/')}>YAML</Link>

                    <Link to={prefixLink('/toml/')}>TOML</Link>

Supported CSS processors

                    <Link to={prefixLink('/postcss/')}>PostCSS</Link>

                    <Link to={prefixLink('/css-modules/')}>CSS Modules</Link>

                    <Link to={prefixLink('/sass/')}>Sass</Link>

                    <Link to={prefixLink('/less/')}>Less</Link>

                )
                }
                }
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { ScrollView, View, StyleSheet} from 'react-native';
import ItemView from './ItemView'

var styles = StyleSheet.create({
  root: {
    flex:1,
    alignSelf:'stretch'
  },
  container: {
    alignSelf:'stretch'
  },
  header:{ }
})

export default class Waterfall extends Component{
  static defaultProps = {
    onEndReachedThreshold: 0,
    expansionOfScope: 500,
    numberOfColumns: 3,
    data: []
  };
  constructor(props){
    super(props);
    this.list = [...props.data];
    this._resetColHeight();
    this.itemRefs = [];
    this.visibleRange = { start: 0, end: 0 };
    this.curPlacingIdx = 0;
    this.placing = this.list.length > 0;
    this.headerHeight = 0;
    this.headerWidth = 0;
  }

  componentWillReceiveProps(nextProps){
    var { data } = nextProps;
    if( !data instanceof Array ){
      var err = new Error("props.data should be an array!");
      return console.error(err);
    }
    this.placing = true

    //datas have been refresh, remove all items.
    if( data !== this.props.data ){
      //reset placement job, start with the first item.
      this.curPlacingIdx = 0;
      //reset the list to remove all items
      this.list = [];
      //reset items' reference
      this.itemRefs = [];
      //reset every columns' height
      this._resetColHeight()
      //reset visible range
      this._resetVisibleRange()
      //fire 'render' to remove all itemViews
      this.forceUpdate()

      this.scrollView.scrollTo({x: 0, y: 0, animated: false})
    }
    //add more item
    else if(data.length > this.list.length){
      for(var i=this.list.length; i< data.length; ++i){
        this.list.push(data[i])
      }
      this.forceUpdate()
    }
    else if(data.length < this.list.length){
      var err = new Error("does not support remove item!");
      return console.error(err);
    }
    else{

    }
  }
  shouldComponentUpdate(nextProps){
    var { data } = nextProps;
    if( data !== this.props.data )
      return true

    return false
  }
  componentDidUpdate(lastProps,lastState){
    //datas have been refresh and all items have been removed.
    //add items again.
    if(this.list.length === 0 && this.props.data.length > 0){
      this.list = [...this.props.data];
      this.forceUpdate()
      this._controlItemVisibility(0)
    }
  }
  _resetVisibleRange(){
    this.visibleRange.start = 0;
    this.visibleRange.end = 0;
    delete this.lastContentOffset;
  }
  _resetColHeight(){
    var { numberOfColumns } = this.props;
    this.colHeights = [];
    for(let i=0; i < numberOfColumns; ++i){
      this.colHeights.push(0)
    }
  }
  componentWillUpdate(lastProps,lastState){}
  _getItemWidth(){
    var { numberOfColumns } = this.props
    return this.width/numberOfColumns;
  }
  _getMaxHeight(){
    var maxCol = this._getMaxCol();
    return this.headerHeight + this.colHeights[maxCol];
  }
  _getMinHeight(){
    var minCol = this._getMinCol();
    return this.headerHeight + this.colHeights[minCol];
  }
  _getMaxCol(){
    var numberOfColumns = 0;
    var max = this.colHeights[0];
    for(var i=0; i<this.colHeights.length; ++i){
      if(this.colHeights[i] > max){
        numberOfColumns = i;;
        max = this.colHeights[i];
      }
    }
    return numberOfColumns;
  }
  _getMinCol(){
    var numberOfColumns = 0;
    var min = this.colHeights[0];
    for(var i=0; i<this.colHeights.length; ++i){
      if(this.colHeights[i] < min){
        numberOfColumns = i;
        min = this.colHeights[i];
      }
    }
    return numberOfColumns;
  }
  _renderItems(){
    return this.list.map(this._renderItem);
  }
  _renderItem = (item,idx)=>{
    var { renderItem, numberOfColumns } = this.props;
    var ref = (ref)=>{ this._refItem(ref,idx) };
    var initStyle = {
      //opacity: 0,
      position:'absolute',
      width: this._getItemWidth(),
    };
    return (
      <ItemView
        parent={this}
        idx={idx}
        item={item}
        ref={ref}
        key={idx}
        style={initStyle}
        renderContent={renderItem}/>
    );
  }
  _refItem = (ref,idx)=>{
    this.itemRefs[idx] = ref;
  }

  placeItem(itemRef){
    var placementJob = (itemRef)=>{
      var minCol = this._getMinCol();
      var left   = minCol*this._getItemWidth();
      var top    = this._getMinHeight();
      itemRef.setPosition(left,top);
      itemRef.showUp();
      this.colHeights[minCol] += itemRef.height;
      //重新设置内部容器高度
      this.container.setNativeProps({
        style:{
          height: this._getMaxHeight()
        }
      })
      ++this.curPlacingIdx;
      if( this.curPlacingIdx === this.list.length){
        this.placing = false;
        this.jobAfterPlacing && this.jobAfterPlacing();
        this.jobAfterPlacing = null;
      }
    }
    if( itemRef.props.idx == 0){
      this.curPlacingIdx = 0
      this._resetVisibleRange()
      this._resetColHeight()
    }
    //轮到当前组件做渲染。
    if( itemRef.props.idx == this.curPlacingIdx ){
      placementJob(itemRef);
    }
    //前面还有item重新放置，需要等待。
    else if(itemRef.props.idx > this.curPlacingIdx){

    }
    else if(itemRef.props.idx < this.curPlacingIdx){

    }
  }
  _onRootLayout = (e)=>{
    this.props.onLayout && this.props.onLayout(e)
    var { width, height } = e.nativeEvent.layout
    this.width = width
    this.height = height
  }
  _onHeaderLayout = (e)=>{
    this.props.onHeaderLayout && this.props.onHeaderLayout(e)
    var { width, height } = e.nativeEvent.layout
    this.headerWidth = width
    this.headerHeight = height
  }
  _refScrollView = (ref)=>{ this.scrollView = ref; }
  _refContainer = (ref)=>{ this.container = ref; }
  _refHeader = (ref)=> { this.header = ref; }
  _controlItemVisibility = (contentOffsetY)=>{
    var expansionOfScope = this.props.expansionOfScope || 0;
    var top = contentOffsetY - expansionOfScope;
    var bottom = contentOffsetY + this.height + expansionOfScope;
    var { start, end } = this.visibleRange;
    if(this.lastContentOffset === undefined){
      for(var i = 0; i < this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }
        if( top > itemView.getFoot() ){
          itemView.hideIt();
          this.visibleRange.start = i+1;
        }
        else if( bottom < itemView.getTop() ){
          itemView.hideIt();
        }
        else{
          itemView.showIt();
          this.visibleRange.end = i;
        }
      }
    }
    //items move upward
    else if( contentOffsetY > this.lastContentOffset){
      for(var i = start; i<this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( top > itemView.getFoot() ){
          itemView.hideIt();
        }else{
          this.visibleRange.start = i;
          break;
        }
      }
      for(var i=end; i < this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( bottom > itemView.getTop() ){
          itemView.showIt();
          this.visibleRange.end = i;
        }else{
          break;
        }
      }
    }
    //items move downward
    if( contentOffsetY < this.lastContentOffset){
      for(var i=start; i>=0; --i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( top < itemView.getFoot() ){
          itemView.showIt();
          this.visibleRange.start = i;
        }else{
          break;
        }
      }
      for(var i=end; i>=0; --i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( bottom < itemView.getTop()  ){
          itemView.hideIt();
        }else{
          this.visibleRange.end = i;
          break;
        }
      }
    }
    this.lastContentOffset = contentOffsetY;
  }
  _onScroll = (e)=>{
    var { onScroll, onEndReached, onEndReachedThreshold } = this.props;
    var { nativeEvent } = e;
    var { contentOffset , layoutMeasurement, contentSize} = nativeEvent;

    this._controlItemVisibility( contentOffset.y );
    onScroll && onScroll(e);

    if( this.isHandlingOnEndReached && contentSize.height != this.lastContentHeight){
      this.isHandlingOnEndReached = false;
    }
    if( !this.isHandlingOnEndReached && contentSize.height - contentOffset.y - layoutMeasurement.height < onEndReachedThreshold ){
      this.isHandlingOnEndReached = true;
      this.lastContentHeight = contentSize.height;
      onEndReached && onEndReached();
    }else if( this.isHandlingOnEndReached && contentSize.height - contentOffset.y - layoutMeasurement.height > onEndReachedThreshold){
      this.isHandlingOnEndReached = false;
    }
  }
  _renderHeader = ()=>{
    return (
      <View style={styles.header} ref={this._refHeader} onLayout={this._onHeaderLayout}>
        { this.props.renderHeader && this.props.renderHeader() }
      </View>
    )
  }
  render(){
    return (
      <ScrollView {...this.props} ref={this._refScrollView} style={[styles.root,this.props.style]} onLayout={this._onRootLayout} onScroll={this._onScroll}>
        <View style={styles.container} ref={this._refContainer} >
        { this._renderHeader() }
        { this.width && this._renderItems() }
        </View>
      </ScrollView>
    );
  }
}
<commit_msg>添加props gap，设置item间隔<commit_after>import React, { Component } from 'react';
import { ScrollView, View, StyleSheet} from 'react-native';
import ItemView from './ItemView'

var styles = StyleSheet.create({
  root: {
    flex:1,
    alignSelf:'stretch'
  },
  container: {
    alignSelf:'stretch'
  },
  header:{ }
})

export default class Waterfall extends Component{
  static defaultProps = {
    onEndReachedThreshold: 0,
    expansionOfScope: 500,
    numberOfColumns: 3,
    data: []
  };
  constructor(props){
    super(props);
    this.list = [...props.data];
    this._resetColHeight();
    this.itemRefs = [];
    this.visibleRange = { start: 0, end: 0 };
    this.curPlacingIdx = 0;
    this.headerHeight = 0;
    this.headerWidth = 0;
  }

  componentWillReceiveProps(nextProps){
    var { data } = nextProps;
    if( !data instanceof Array ){
      var err = new Error("props.data should be an array!");
      return console.error(err);
    }

    //datas have been refresh, remove all items.
    if( data !== this.props.data ){
      //reset placement job, start with the first item.
      this.curPlacingIdx = 0;
      //reset the list to remove all items
      this.list = [];
      //reset items' reference
      this.itemRefs = [];
      //reset every columns' height
      this._resetColHeight()
      //reset visible range
      this._resetVisibleRange()
      //fire 'render' to remove all itemViews
      this.forceUpdate()

      this.scrollView.scrollTo({x: 0, y: 0, animated: false})
    }
    //add more item
    else if(data.length > this.list.length){
      for(var i=this.list.length; i< data.length; ++i){
        this.list.push(data[i])
      }
      this.forceUpdate()
    }
    else if(data.length < this.list.length){
      var err = new Error("does not support remove item!");
      return console.error(err);
    }
    else{

    }
  }
  shouldComponentUpdate(nextProps){
    var { data } = nextProps;
    if( data !== this.props.data )
      return true

    return false
  }
  componentDidUpdate(lastProps,lastState){
    //datas have been refresh and all items have been removed.
    //add items again.
    if(this.list.length === 0 && this.props.data.length > 0){
      this.list = [...this.props.data];
      this.forceUpdate()
      this._controlItemVisibility(0)
    }
  }
  _resetVisibleRange(){
    this.visibleRange.start = 0;
    this.visibleRange.end = 0;
    delete this.lastContentOffset;
  }
  _resetColHeight(){
    var { numberOfColumns } = this.props;
    this.colHeights = [];
    for(let i=0; i < numberOfColumns; ++i){
      this.colHeights.push(0)
    }
  }
  componentWillUpdate(lastProps,lastState){}
  _getItemWidth(){
    var { numberOfColumns, gap } = this.props
    if( gap > 0){
      return (this.width - gap * (1 + numberOfColumns)) / numberOfColumns
    }
    else{
      return this.width/numberOfColumns
    }
  }
  _getMaxHeight(){
    var maxCol = this._getMaxCol();
    return this.headerHeight + this.colHeights[maxCol];
  }
  _getMinHeight(){
    var minCol = this._getMinCol();
    var { gap } = this.props
    if( gap > 0 ){
      return gap + this.headerHeight + this.colHeights[minCol]
    }else{
      return this.headerHeight + this.colHeights[minCol];
    }
  }
  _getMaxCol(){
    var numberOfColumns = 0;
    var max = this.colHeights[0];
    for(var i=0; i<this.colHeights.length; ++i){
      if(this.colHeights[i] > max){
        numberOfColumns = i;;
        max = this.colHeights[i];
      }
    }
    return numberOfColumns;
  }
  _getMinCol(){
    var numberOfColumns = 0;
    var min = this.colHeights[0];
    for(var i=0; i<this.colHeights.length; ++i){
      if(this.colHeights[i] < min){
        numberOfColumns = i;
        min = this.colHeights[i];
      }
    }
    return numberOfColumns;
  }
  _renderItems(){
    return this.list.map(this._renderItem);
  }
  _renderItem = (item,idx)=>{
    var { renderItem, numberOfColumns } = this.props;
    var ref = (ref)=>{ this._refItem(ref,idx) };
    var initStyle = {
      position:'absolute',
      width: this._getItemWidth(),
    };
    return (
      <ItemView
        parent={this}
        idx={idx}
        item={item}
        ref={ref}
        key={idx}
        style={initStyle}
        renderContent={renderItem}/>
    );
  }
  _refItem = (ref,idx)=>{
    this.itemRefs[idx] = ref;
  }

  placeItem(itemRef){
    var placementJob = (itemRef)=>{
      var minCol = this._getMinCol();
      var left   = minCol*this._getItemWidth();

      var { gap } = this.props;
      if( gap > 0 ){
        left += (minCol+1)*gap;
      }

      var top    = this._getMinHeight();
      itemRef.setPosition(left,top);
      itemRef.showUp();

      this.colHeights[minCol] += itemRef.height;

      var { gap } = this.props;
      if( gap > 0 ){
        this.colHeights[minCol] += gap;
      }

      //重新设置内部容器高度
      this.container.setNativeProps({
        style:{
          height: this._getMaxHeight()
        }
      });
      ++this.curPlacingIdx;
      if( this.curPlacingIdx === this.list.length){
        this.jobAfterPlacing && this.jobAfterPlacing();
        this.jobAfterPlacing = null;
      }
    }
    if( itemRef.props.idx == 0){
      this.curPlacingIdx = 0;
      this._resetVisibleRange();
      this._resetColHeight();
    }
    //轮到当前组件做渲染。
    if( itemRef.props.idx == this.curPlacingIdx ){
      placementJob(itemRef);
    }
    //前面还有item重新放置，需要等待。
    else if(itemRef.props.idx > this.curPlacingIdx){

    }
    else if(itemRef.props.idx < this.curPlacingIdx){

    }
  }
  _onRootLayout = (e)=>{
    this.props.onLayout && this.props.onLayout(e);
    var { width, height } = e.nativeEvent.layout;
    this.width = width;
    this.height = height;
  }
  _onHeaderLayout = (e)=>{
    this.props.onHeaderLayout && this.props.onHeaderLayout(e);
    var { width, height } = e.nativeEvent.layout;
    this.headerWidth = width;
    this.headerHeight = height;
  }
  _refScrollView = (ref)=>{ this.scrollView = ref; }
  _refContainer = (ref)=>{ this.container = ref; }
  _refHeader = (ref)=> { this.header = ref; }
  _controlItemVisibility = (contentOffsetY)=>{
    var expansionOfScope = this.props.expansionOfScope || 0;
    var top = contentOffsetY - expansionOfScope;
    var bottom = contentOffsetY + this.height + expansionOfScope;
    var { start, end } = this.visibleRange;
    if(this.lastContentOffset === undefined){
      for(var i = 0; i < this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }
        if( top > itemView.getFoot() ){
          itemView.hideIt();
          this.visibleRange.start = i+1;
        }
        else if( bottom < itemView.getTop() ){
          itemView.hideIt();
        }
        else{
          itemView.showIt();
          this.visibleRange.end = i;
        }
      }
    }
    //items move upward
    else if( contentOffsetY > this.lastContentOffset){
      for(var i = start; i<this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( top > itemView.getFoot() ){
          itemView.hideIt();
        }else{
          this.visibleRange.start = i;
          break;
        }
      }
      for(var i=end; i < this.itemRefs.length; ++i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( bottom > itemView.getTop() ){
          itemView.showIt();
          this.visibleRange.end = i;
        }else{
          break;
        }
      }
    }
    //items move downward
    if( contentOffsetY < this.lastContentOffset){
      for(var i=start; i>=0; --i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( top < itemView.getFoot() ){
          itemView.showIt();
          this.visibleRange.start = i;
        }else{
          break;
        }
      }
      for(var i=end; i>=0; --i){
        var itemView = this.itemRefs[i];
        if( !itemView ){
          continue
        }if( bottom < itemView.getTop()  ){
          itemView.hideIt();
        }else{
          this.visibleRange.end = i;
          break;
        }
      }
    }
    this.lastContentOffset = contentOffsetY;
  }
  _onScroll = (e)=>{
    var { onScroll, onEndReached, onEndReachedThreshold } = this.props;
    var { nativeEvent } = e;
    var { contentOffset , layoutMeasurement, contentSize} = nativeEvent;

    this._controlItemVisibility( contentOffset.y );
    onScroll && onScroll(e);

    if( this.isHandlingOnEndReached && contentSize.height != this.lastContentHeight){
      this.isHandlingOnEndReached = false;
    }
    if( !this.isHandlingOnEndReached && contentSize.height - contentOffset.y - layoutMeasurement.height < onEndReachedThreshold ){
      this.isHandlingOnEndReached = true;
      this.lastContentHeight = contentSize.height;
      onEndReached && onEndReached();
    }else if( this.isHandlingOnEndReached && contentSize.height - contentOffset.y - layoutMeasurement.height > onEndReachedThreshold){
      this.isHandlingOnEndReached = false;
    }
  }
  _renderHeader = ()=>{
    return (
      <View style={styles.header} ref={this._refHeader} onLayout={this._onHeaderLayout}>
        { this.props.renderHeader && this.props.renderHeader() }
      </View>
    )
  }
  render(){
    return (
      <ScrollView {...this.props} ref={this._refScrollView} style={[styles.root,this.props.style]} onLayout={this._onRootLayout} onScroll={this._onScroll}>
        <View style={styles.container} ref={this._refContainer} >
        { this._renderHeader() }
        { this.width && this._renderItems() }
        </View>
      </ScrollView>
    );
  }
}
<|endoftext|>


<commit_before>Meteor.methods({
    updateAvatar: function(base64) {
        var id = this.userId;
        if (!id) {
            throw new Meteor.Error(403, "You must be logged in");
        }
        try {
            validateImgBase64(base64);
            return Meteor.users.update({_id: id},
                {$set: {'profile.cover_image': base64, 'profile.upgraded': new Date()}}
            );
        }
        catch(e){
            throw new Meteor.Error(403, e.message);
        }
        return true;
    }
});
<commit_msg>validate and save cover image<commit_after>Meteor.methods({
    updateCover: function(base64) {
        var id = this.userId;
        if (!id) {
            throw new Meteor.Error(403, "You must be logged in");
        }
        try {
            validateImgBase64(base64);
            return Meteor.users.update({_id: id},
                {$set: {'profile.cover_image': base64, 'profile.upgraded': new Date()}}
            );
        }
        catch(e){
            throw new Meteor.Error(403, e.message);
        }
        return true;
    }
});
<|endoftext|>


<commit_before>var buster = require("buster-core");
var busterSessionMiddlewareSession = require("./session");

module.exports = buster.extend(buster.eventEmitter.create(), {
    create: function (busterResources, server) {
        var instance = Object.create(this);
        instance.busterResources = busterResources;
        instance.server = server;
        instance.sessions = [];
        return instance;
    },

    respond: function (req, res) {
        if (req.method == "POST" && req.url == "/sessions") {
            this.logger.debug("Creating session via HTTP");
            createSessionFromRequest.call(this, req, res);
            return true;
        }

        for (var i = 0, ii = this.sessions.length; i < ii; i++) {
            var session =  this.sessions[i];

            if (req.url == session.rootPath && req.method == "DELETE") {
                this.logger.info("Destroying session via HTTP");
                unloadSession.call(this, i);
                res.writeHead(200);
                res.end();
                return true;
            }
        }
    },

    createSession: function (data) {
        var error = busterSessionMiddlewareSession.validate(data);
        if (error) throw new Error(error);
        this.logger.info("Creating session");
        return createSessionWithData.call(this, data);
    },

    destroySession: function (id) {
        for (var i = 0, ii = this.sessions.length; i < ii; i++) {
            var session = this.sessions[i];
            if (session.id == id) {
                this.logger.info("Destroying session");
                this.logger.debug("Session ID", id)
                unloadSession.call(this, i);
                break;
            }
        }
    },

    destroyCurrentSession: function () {
        if (this.sessions.length > 0) {
            this.destroySession(this.sessions[0].id);
        }
    }
});

function unloadSession(i) {
    var session = this.sessions[i];
    this.busterResources.removeResourceSet(session.resourceSet);
    session.destroy();
    this.sessions.splice(i, 1);

    // Did we unload the current session?
    if (i == 0) {
        this.logger.debug("Ending current session");
        this.emit("session:end");
        if (this.sessions.length > 0) {
            this.logger.debug("Starting queued session");
            loadSession.call(this);
        }
    }
}

function createSessionFromRequest(req, res) {
    var self = this;

    var requestBody = "";
    req.on("data", function (chunk) { requestBody += chunk.toString("utf8") });
    req.on("end", function () {
        createSessionWithRequestBody.call(self, requestBody, req, res);
    });
}

function createSessionWithRequestBody(requestBody, req, res) {
    try {
        var data = JSON.parse(requestBody);
    } catch(e) {
        this.logger.error("Invalid JSON", requestBody);
        return failWithMessage(res, "Invalid JSON");
    }

    var error = busterSessionMiddlewareSession.validate(data);
    if (error) {
        this.logger.error("Invalid session", error);
        return failWithMessage(res, error);
    }

    try {
        var session = createSessionWithData.call(this, data);
    } catch (e) {
        this.logger.error("Failed to create session", e);
        res.writeHead(403);
        res.write(e.message);
        res.end();
        return;
    }

    var statusCode;
    if (this.sessions.length > 1) {
        statusCode = 202;
    } else {
        statusCode = 201;
    }

    res.writeHead(statusCode, {"Location": session.rootPath});
    res.write(JSON.stringify(buster.extend(session.toJSON(), {
        clients: this.server.capture.capturedClients.map(function (client) {
            return {id: client.id}
        })
    })));
    res.end();
}

function createSessionWithData(data) {
    var self = this;

    var resourceSet = this.busterResources.createResourceSet(data.resourceSet || {});
    var session = busterSessionMiddlewareSession.create(resourceSet);

    if ("joinable" in data) {
        session.joinable = data.joinable;
        delete data.joinable;
    } else {
        session.joinable = true;
    }

    this.sessions.push(session);
    if (this.sessions.length == 1) {
        this.logger.debug("Starting newly created session immediately");
        loadSession.call(this);
    } else {
        this.logger.debug("Queuing newly created session");
    }

    session._bayeux.attach(this.server.httpServer);
    session._bayeux.addExtension({
        incoming: function (message, callback) {
            if (message.channel == "/session-owner") {
                self.sessionOwnerFayeClientId = message.clientId;
            }

            callback(message);
        }
    });
    session._bayeux.bind("disconnect", function (clientId) {
        if (clientId == self.sessionOwnerFayeClientId) {
            self.destroySession(session.id);
        }
    });

    return session;
}

function loadSession() {
    this.emit("session:start", this.sessions[0]);
}

function failWithMessage(res, message) {
    res.writeHead(500);
    res.write(message + "\n");
    res.end();
}<commit_msg>It makes sense that the loading of the session is the last operation.<commit_after>var buster = require("buster-core");
var busterSessionMiddlewareSession = require("./session");

module.exports = buster.extend(buster.eventEmitter.create(), {
    create: function (busterResources, server) {
        var instance = Object.create(this);
        instance.busterResources = busterResources;
        instance.server = server;
        instance.sessions = [];
        return instance;
    },

    respond: function (req, res) {
        if (req.method == "POST" && req.url == "/sessions") {
            this.logger.debug("Creating session via HTTP");
            createSessionFromRequest.call(this, req, res);
            return true;
        }

        for (var i = 0, ii = this.sessions.length; i < ii; i++) {
            var session =  this.sessions[i];

            if (req.url == session.rootPath && req.method == "DELETE") {
                this.logger.info("Destroying session via HTTP");
                unloadSession.call(this, i);
                res.writeHead(200);
                res.end();
                return true;
            }
        }
    },

    createSession: function (data) {
        var error = busterSessionMiddlewareSession.validate(data);
        if (error) throw new Error(error);
        this.logger.info("Creating session");
        return createSessionWithData.call(this, data);
    },

    destroySession: function (id) {
        for (var i = 0, ii = this.sessions.length; i < ii; i++) {
            var session = this.sessions[i];
            if (session.id == id) {
                this.logger.info("Destroying session");
                this.logger.debug("Session ID", id)
                unloadSession.call(this, i);
                break;
            }
        }
    },

    destroyCurrentSession: function () {
        if (this.sessions.length > 0) {
            this.destroySession(this.sessions[0].id);
        }
    }
});

function unloadSession(i) {
    var session = this.sessions[i];
    this.busterResources.removeResourceSet(session.resourceSet);
    session.destroy();
    this.sessions.splice(i, 1);

    // Did we unload the current session?
    if (i == 0) {
        this.logger.debug("Ending current session");
        this.emit("session:end");
        if (this.sessions.length > 0) {
            this.logger.debug("Starting queued session");
            loadSession.call(this);
        }
    }
}

function createSessionFromRequest(req, res) {
    var self = this;

    var requestBody = "";
    req.on("data", function (chunk) { requestBody += chunk.toString("utf8") });
    req.on("end", function () {
        createSessionWithRequestBody.call(self, requestBody, req, res);
    });
}

function createSessionWithRequestBody(requestBody, req, res) {
    try {
        var data = JSON.parse(requestBody);
    } catch(e) {
        this.logger.error("Invalid JSON", requestBody);
        return failWithMessage(res, "Invalid JSON");
    }

    var error = busterSessionMiddlewareSession.validate(data);
    if (error) {
        this.logger.error("Invalid session", error);
        return failWithMessage(res, error);
    }

    try {
        var session = createSessionWithData.call(this, data);
    } catch (e) {
        this.logger.error("Failed to create session", e);
        res.writeHead(403);
        res.write(e.message);
        res.end();
        return;
    }

    var statusCode;
    if (this.sessions.length > 1) {
        statusCode = 202;
    } else {
        statusCode = 201;
    }

    res.writeHead(statusCode, {"Location": session.rootPath});
    res.write(JSON.stringify(buster.extend(session.toJSON(), {
        clients: this.server.capture.capturedClients.map(function (client) {
            return {id: client.id}
        })
    })));
    res.end();
}

function createSessionWithData(data) {
    var self = this;

    var resourceSet = this.busterResources.createResourceSet(data.resourceSet || {});
    var session = busterSessionMiddlewareSession.create(resourceSet);

    if ("joinable" in data) {
        session.joinable = data.joinable;
        delete data.joinable;
    } else {
        session.joinable = true;
    }

    session._bayeux.attach(this.server.httpServer);
    session._bayeux.addExtension({
        incoming: function (message, callback) {
            if (message.channel == "/session-owner") {
                self.sessionOwnerFayeClientId = message.clientId;
            }

            callback(message);
        }
    });
    session._bayeux.bind("disconnect", function (clientId) {
        if (clientId == self.sessionOwnerFayeClientId) {
            self.destroySession(session.id);
        }
    });

    this.sessions.push(session);
    if (this.sessions.length == 1) {
        this.logger.debug("Starting newly created session immediately");
        loadSession.call(this);
    } else {
        this.logger.debug("Queuing newly created session");
    }

    return session;
}

function loadSession() {
    this.emit("session:start", this.sessions[0]);
}

function failWithMessage(res, message) {
    res.writeHead(500);
    res.write(message + "\n");
    res.end();
}<|endoftext|>


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

/**
 * @lends webOS
 */

/**
 * Fetches the appID of the caller app
 * @returns {string} AppID of the app.
 */
webOS.fetchAppId = function() {
	if (window.PalmSystem && PalmSystem.identifier) {
		// PalmSystem.identifier: <appid> <processid>
		return PalmSystem.identifier.split(" ")[0];
	}
};

/**
 * @callback webOS~appInfoCallback
 * @param {?object} info - JSON data object read from the app's "appinfo.json" file. Undefined if not found.
 */

/**
 * Fetches the appinfo.json data of the caller app with a cache saved to webOS.appInfo
 * @param {webOS~appInfoCallback} callback - The function to called upon completion
 * @param {string} [path] - A relative filepath from the current document to a specific appinfo to read
 */
webOS.fetchAppInfo = function(callback, path) {
	if(!webOS.appInfo) {
		var appID = this.fetchAppId();
		// Virtually all apps will be at "appinfo.json", but extras help edge cases
		var paths = [
			this.fetchAppRootPath() + "appinfo.json",
			"appinfo.json",
			"file:///media/cryptofs/apps/usr/palm/applications/" + appID + "/appinfo.json",
			"file:///usr/palm/applications/" + appID + "/appinfo.json"
		];
		var index = paths[1].indexOf(appID);
		if(index>-1) { //Possible relative path fix for multiple language apps with multiple documents
			paths.splice(1, 0, paths[1].substring(0, index) + appID + "/appinfo.json");
		}
		path && paths.unshift(path);

		var checkAppInfo = function(parseInfo) {
			if(paths.length==0) {
				parseInfo({status:404});
			} else {
				var curr = paths.shift();
				var req = new XMLHttpRequest();
				req.onreadystatechange = function() {
					if(req.readyState==4) {
						if((req.status >= 200 && req.status < 300) || req.status===0) {
							parseInfo(undefined, req.responseText);
						} else {
							checkAppInfo(parseInfo);
						}
					}
				};
				req.open('GET', curr, true);
				req.send(null);
			}
		};
		checkAppInfo(function(err, info) {
			if(!err && info) {
				try {
					webOS.appInfo = JSON.parse(info);
					callback && callback(webOS.appInfo);
				} catch(e) {
					console.error("Unable to parse appinfo.json file for " + appID);
					callback && callback();
				}
			} else {
				console.error("Unable to find appinfo.json file for " + appID);
				callback && callback();
			}
		});
	} else {
		callback && callback(webOS.appInfo);
	}
};

/**
 * Fetches the full root URI path of the caller app
 * @returns {string} App's URI path the app is within.
 */
webOS.fetchAppRootPath = function() {
	var base = window.location.href;
	if('baseURI' in window.document) {
		base = window.document.baseURI;
	} else {
		var baseTags = window.document.getElementsByTagName("base");
		if(baseTags.length > 0) {
			base = baseTags[0].href;
		}
	}
	var match = base.match(new RegExp(".*:\/\/[^#]*\/"));
	if(match) {
		return match[0];
	}
	return "";
};
<commit_msg>Catch exception from failed requests due to security policy<commit_after>/*
 * Copyright (c) 2013-2015 LG Electronics
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

/**
 * @lends webOS
 */

/**
 * Fetches the appID of the caller app
 * @returns {string} AppID of the app.
 */
webOS.fetchAppId = function() {
	if (window.PalmSystem && PalmSystem.identifier) {
		// PalmSystem.identifier: <appid> <processid>
		return PalmSystem.identifier.split(" ")[0];
	}
};

/**
 * @callback webOS~appInfoCallback
 * @param {?object} info - JSON data object read from the app's "appinfo.json" file. Undefined if not found.
 */

/**
 * Fetches the appinfo.json data of the caller app with a cache saved to webOS.appInfo
 * @param {webOS~appInfoCallback} callback - The function to called upon completion
 * @param {string} [path] - A relative filepath from the current document to a specific appinfo to read
 */
webOS.fetchAppInfo = function(callback, path) {
	if(!webOS.appInfo) {
		var appID = this.fetchAppId();
		// Virtually all apps will be at "appinfo.json", but extras help edge cases
		var paths = [
			this.fetchAppRootPath() + "appinfo.json",
			"appinfo.json",
			"file:///media/cryptofs/apps/usr/palm/applications/" + appID + "/appinfo.json",
			"file:///usr/palm/applications/" + appID + "/appinfo.json"
		];
		var index = paths[1].indexOf(appID);
		if(index>-1) { //Possible relative path fix for multiple language apps with multiple documents
			paths.splice(1, 0, paths[1].substring(0, index) + appID + "/appinfo.json");
		}
		path && paths.unshift(path);

		var checkAppInfo = function(parseInfo) {
			if(paths.length==0) {
				parseInfo({status:404});
			} else {
				var curr = paths.shift();
				var req = new XMLHttpRequest();
				req.onreadystatechange = function() {
					if(req.readyState==4) {
						if((req.status >= 200 && req.status < 300) || req.status===0) {
							parseInfo(undefined, req.responseText);
						} else {
							checkAppInfo(parseInfo);
						}
					}
				};
				try {
					req.open('GET', curr, true);
					req.send(null);
				} catch(e) {
					checkAppInfo(parseInfo);
				}
			}
		};
		checkAppInfo(function(err, info) {
			if(!err && info) {
				try {
					webOS.appInfo = JSON.parse(info);
					callback && callback(webOS.appInfo);
				} catch(e) {
					console.error("Unable to parse appinfo.json file for " + appID);
					callback && callback();
				}
			} else {
				callback && callback();
			}
		});
	} else {
		callback && callback(webOS.appInfo);
	}
};

/**
 * Fetches the full root URI path of the caller app
 * @returns {string} App's URI path the app is within.
 */
webOS.fetchAppRootPath = function() {
	var base = window.location.href;
	if('baseURI' in window.document) {
		base = window.document.baseURI;
	} else {
		var baseTags = window.document.getElementsByTagName("base");
		if(baseTags.length > 0) {
			base = baseTags[0].href;
		}
	}
	var match = base.match(new RegExp(".*:\/\/[^#]*\/"));
	if(match) {
		return match[0];
	}
	return "";
};
<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('lodash');
var PaystationEmbedApp = require('paystation-embed-app');
var Api = require('./api');
var Exception = require('exception');
var React = require('react');
var ReactDOM = require('react-dom');
var Translate = require('./translate');

module.exports = (function () {
    function App() {
        this.config = _.extend({}, DEFAULT_CONFIG);
        this.eventObject = $({});
        this.isInitiated = false;
        this.targetElement = null;
    }

    var DEFAULT_CONFIG = {
        access_token: null,
        access_data: null,
        theme: {
            foreground : 'light',
            background : 'blue'
        },
        template: 'tiny',
        host: 'secure.xsolla.com'
    };

    App.eventTypes = _.extend({}, PaystationEmbedApp.eventTypes);

    /** Private Members **/
    App.prototype.config = {};
    App.prototype.isInitiated = false;
    App.prototype.eventObject = $({});

    App.prototype.checkConfig = function () {
        if (_.isEmpty(this.config.access_token) && _.isEmpty(this.config.access_data)) {
            this.throwError('No access token or access data given');
        }

        if (!_.isEmpty(this.config.access_data) && !_.isPlainObject(this.config.access_data)) {
            this.throwError('Invalid access data format');
        }

        if (_.isEmpty(this.config.target_element)) {
            this.throwError('No target element given');
        }

        if (!$(this.config.target_element).length) {
            this.throwError('Target element doesn\'t exist in the DOM');
        }

        if (_.isEmpty(this.config.host)) {
            this.throwError('Invalid host');
        }

        if (this.config.theme.background !== 'dark' && this.config.theme.background !== 'light') {
            this.config.theme.background = 'light';
        }

        var colorForeground = ["red", "green", "gold", "blue"];
        if (colorForeground.indexOf(this.config.theme.foreground) === -1 ) {
            this.config.theme.foreground = "blue";
        }
    };

    App.prototype.checkApp = function () {
        if (_.isUndefined(this.isInitiated)) {
            this.throwError('Initialize widget before opening');
        }
    };

    App.prototype.throwError = function (message) {
        throw new Exception(message);
    };

    App.prototype.triggerEvent = function () {
        this.eventObject.trigger.apply(this.eventObject, arguments);
    };

    App.prototype.setUpTheme = function () {
        if (this.config.theme.background === 'dark') {
            require('./styles/widget-dark.scss');
        } else {
            require('./styles/widget-light.scss');
        }
    };

    /**
     * Initialize widget with options
     * @param options
     */
    App.prototype.init = function (options) {
        this.isInitiated = true;
        this.config = _.extend({}, DEFAULT_CONFIG, options);
        this.checkConfig();
        this.setUpTheme();

        this.targetElement = $(options.target_element);

        var request = {
            fail_locale: 'en'
        };

        if (options.access_token) {
            request.access_token = options.access_token;
        } else {
            request.access_data = JSON.stringify(options.access_data);
        }

        this.api = new Api(request, {
            sandbox: options.sandbox,
            host: this.config.host
        });

        this.render();

        this.triggerEvent('init');
    };

    /**
     * Open payment interface (PayStation)
     */
    App.prototype.open = function (params) {
        this.checkApp();

        var access_data = {purchase: {tips: undefined}};

        if (params.tips && params.tips.amount && params.tips.currency) {
            access_data.purchase.tips = params.tips;
        }

        PaystationEmbedApp.init({
            access_token: this.config.access_token,
            access_data: _.merge(access_data, this.config.access_data),
            sandbox: this.config.sandbox,
            lightbox: this.config.lightbox,
            childWindow: this.config.childWindow,
            host: this.config.host
        });

        // Register events (forwarding)
        var events = _.values(App.eventTypes).join(' ');
        var eventHandler = _.bind(function () {
            this.triggerEvent.apply(this, arguments);
        }, this);
        PaystationEmbedApp.on(events, eventHandler);

        var openHandler = _.bind(function (event) {
            var instanceId = (params || {}).instance_id;
            if (instanceId) {
                PaystationEmbedApp.sendMessage('set-data', {
                    settings: {
                        payment_method: instanceId
                    }
                });
            }
        }, this);
        PaystationEmbedApp.on('load', openHandler);

        // Unregister events
        PaystationEmbedApp.on('close', _.bind(function (event) {
            PaystationEmbedApp.off(event);
            PaystationEmbedApp.off('load', openHandler);
            PaystationEmbedApp.off(events, eventHandler);
        }, this));

        PaystationEmbedApp.open();
    };

    /**
     * Attach an event handler function for one or more events to the widget
     * @param event One or more space-separated event types (init, open, load, close, status, status-invoice, status-delivering, status-troubled, status-done)
     * @param handler A function to execute when the event is triggered
     */
    App.prototype.on = function (event, handler) {
        if (!_.isFunction(handler)) {
            return;
        }

        this.eventObject.on(event, handler);
    };

    /**
     * Remove an event handler
     * @param event One or more space-separated event types
     * @param handler A handler function previously attached for the event(s)
     */
    App.prototype.off = function (event, handler) {
        this.eventObject.off(event, handler);
    };

    /**
     * Render widget template
     */
    App.prototype.render = function () {
        var view;

        switch (this.config.template) {
            case 'full':
                view = require('./views/layouts/full.jsx');
                break;
            case 'compact':
                view = require('./views/layouts/compact.jsx');
                break;
            case 'tiny':
            default:
                view = require('./views/layouts/tiny.jsx');
                break;
        }

        var props = {
            data: {},
            onPaymentOpen: _.bind(function (params) {
                this.open(params);
            }, this),
            paymentButtonColor : this.config.theme.foreground
        };

        var updateView = _.bind(function () {
            ReactDOM.render(React.createElement(view, props), this.targetElement.get(0));
        }, this);

        updateView();

        this.api.request('pay2play/init').done(function (data) {
            var info = data.digital_content || {};

            props.data = {
                amount: {
                    value: info.min_amount,
                    currency: info.min_currency,
                    hasDifferent: _.uniq(_.pluck(info.drm, 'amount')).length > 1 || _.uniq(_.pluck(info.drm, 'currency')).length > 1
                },
                tips: info.tips,
                name: info.name,
                description: info.description,
                systemRequirements: info.system_requirements,
                drm: info.drm,
                logoUrl: info.image_url,
                paymentList: data.payment_instances
            };

            Translate.init(data.translates || {});

            updateView();
        }).fail(_.bind(function (errors) {
            props.data = {
                errors: errors
            };

            _.each(errors, function (error) {
                if (error.message) {
                    console.warn('XsollaPay2PlayWidget', error.support_code, error.message);
                }
            });

            updateView();
        }, this));
    };

    return App;
})();
<commit_msg>PSPRODUCT-3993 new tips versions fix color theme<commit_after>var $ = require('jquery');
var _ = require('lodash');
var PaystationEmbedApp = require('paystation-embed-app');
var Api = require('./api');
var Exception = require('exception');
var React = require('react');
var ReactDOM = require('react-dom');
var Translate = require('./translate');

module.exports = (function () {
    function App() {
        this.config = _.extend({}, DEFAULT_CONFIG);
        this.eventObject = $({});
        this.isInitiated = false;
        this.targetElement = null;
    }

    var DEFAULT_CONFIG = {
        access_token: null,
        access_data: null,
        theme: {
            foreground : 'blue',
            background : 'light'
        },
        template: 'tiny',
        host: 'secure.xsolla.com'
    };

    App.eventTypes = _.extend({}, PaystationEmbedApp.eventTypes);

    /** Private Members **/
    App.prototype.config = {};
    App.prototype.isInitiated = false;
    App.prototype.eventObject = $({});

    App.prototype.checkConfig = function () {
        if (_.isEmpty(this.config.access_token) && _.isEmpty(this.config.access_data)) {
            this.throwError('No access token or access data given');
        }

        if (!_.isEmpty(this.config.access_data) && !_.isPlainObject(this.config.access_data)) {
            this.throwError('Invalid access data format');
        }

        if (_.isEmpty(this.config.target_element)) {
            this.throwError('No target element given');
        }

        if (!$(this.config.target_element).length) {
            this.throwError('Target element doesn\'t exist in the DOM');
        }

        if (_.isEmpty(this.config.host)) {
            this.throwError('Invalid host');
        }

        if (this.config.theme.background !== 'dark' && this.config.theme.background !== 'light') {
            this.config.theme.background = 'light';
        }

        var colorForeground = ["red", "green", "gold", "blue"];
        if (colorForeground.indexOf(this.config.theme.foreground) === -1 ) {
            this.config.theme.foreground = "blue";
        }
    };

    App.prototype.checkApp = function () {
        if (_.isUndefined(this.isInitiated)) {
            this.throwError('Initialize widget before opening');
        }
    };

    App.prototype.throwError = function (message) {
        throw new Exception(message);
    };

    App.prototype.triggerEvent = function () {
        this.eventObject.trigger.apply(this.eventObject, arguments);
    };

    App.prototype.setUpTheme = function () {
        if (this.config.theme.background === 'dark') {
            require('./styles/widget-dark.scss');
        } else {
            require('./styles/widget-light.scss');
        }
    };

    /**
     * Initialize widget with options
     * @param options
     */
    App.prototype.init = function (options) {
        this.isInitiated = true;
        this.config = _.extend({}, DEFAULT_CONFIG, options);
        this.checkConfig();
        this.setUpTheme();

        this.targetElement = $(options.target_element);

        var request = {
            fail_locale: 'en'
        };

        if (options.access_token) {
            request.access_token = options.access_token;
        } else {
            request.access_data = JSON.stringify(options.access_data);
        }

        this.api = new Api(request, {
            sandbox: options.sandbox,
            host: this.config.host
        });

        this.render();

        this.triggerEvent('init');
    };

    /**
     * Open payment interface (PayStation)
     */
    App.prototype.open = function (params) {
        this.checkApp();

        var access_data = {purchase: {tips: undefined}};

        if (params.tips && params.tips.amount && params.tips.currency) {
            access_data.purchase.tips = params.tips;
        }

        PaystationEmbedApp.init({
            access_token: this.config.access_token,
            access_data: _.merge(access_data, this.config.access_data),
            sandbox: this.config.sandbox,
            lightbox: this.config.lightbox,
            childWindow: this.config.childWindow,
            host: this.config.host
        });

        // Register events (forwarding)
        var events = _.values(App.eventTypes).join(' ');
        var eventHandler = _.bind(function () {
            this.triggerEvent.apply(this, arguments);
        }, this);
        PaystationEmbedApp.on(events, eventHandler);

        var openHandler = _.bind(function (event) {
            var instanceId = (params || {}).instance_id;
            if (instanceId) {
                PaystationEmbedApp.sendMessage('set-data', {
                    settings: {
                        payment_method: instanceId
                    }
                });
            }
        }, this);
        PaystationEmbedApp.on('load', openHandler);

        // Unregister events
        PaystationEmbedApp.on('close', _.bind(function (event) {
            PaystationEmbedApp.off(event);
            PaystationEmbedApp.off('load', openHandler);
            PaystationEmbedApp.off(events, eventHandler);
        }, this));

        PaystationEmbedApp.open();
    };

    /**
     * Attach an event handler function for one or more events to the widget
     * @param event One or more space-separated event types (init, open, load, close, status, status-invoice, status-delivering, status-troubled, status-done)
     * @param handler A function to execute when the event is triggered
     */
    App.prototype.on = function (event, handler) {
        if (!_.isFunction(handler)) {
            return;
        }

        this.eventObject.on(event, handler);
    };

    /**
     * Remove an event handler
     * @param event One or more space-separated event types
     * @param handler A handler function previously attached for the event(s)
     */
    App.prototype.off = function (event, handler) {
        this.eventObject.off(event, handler);
    };

    /**
     * Render widget template
     */
    App.prototype.render = function () {
        var view;

        switch (this.config.template) {
            case 'full':
                view = require('./views/layouts/full.jsx');
                break;
            case 'compact':
                view = require('./views/layouts/compact.jsx');
                break;
            case 'tiny':
            default:
                view = require('./views/layouts/tiny.jsx');
                break;
        }

        var props = {
            data: {},
            onPaymentOpen: _.bind(function (params) {
                this.open(params);
            }, this),
            paymentButtonColor : this.config.theme.foreground
        };

        var updateView = _.bind(function () {
            ReactDOM.render(React.createElement(view, props), this.targetElement.get(0));
        }, this);

        updateView();

        this.api.request('pay2play/init').done(function (data) {
            var info = data.digital_content || {};

            props.data = {
                amount: {
                    value: info.min_amount,
                    currency: info.min_currency,
                    hasDifferent: _.uniq(_.pluck(info.drm, 'amount')).length > 1 || _.uniq(_.pluck(info.drm, 'currency')).length > 1
                },
                tips: info.tips,
                name: info.name,
                description: info.description,
                systemRequirements: info.system_requirements,
                drm: info.drm,
                logoUrl: info.image_url,
                paymentList: data.payment_instances
            };

            Translate.init(data.translates || {});

            updateView();
        }).fail(_.bind(function (errors) {
            props.data = {
                errors: errors
            };

            _.each(errors, function (error) {
                if (error.message) {
                    console.warn('XsollaPay2PlayWidget', error.support_code, error.message);
                }
            });

            updateView();
        }, this));
    };

    return App;
})();
<|endoftext|>


<commit_before>// uniq <https://github.com/mikolalysenko/uniq>
// Copyright (c) 2013 Mikola Lysenko (MIT)

'use strict'

function unique (list, compare, sorted) {
  if (list.length === 0) return list

  if (compare) {
    if (!sorted) list.sort(compare)
    return unique_pred(list, compare)
  }

  if (!sorted) list.sort()
  return unique_eq(list)
}

function unique_pred (list, compare) {
  var ptr = 1
  var len = list.length
  var a = list[0]
  var b = list[0]

  for (var i = 1; i < len; ++i) {
    b = a
    a = list[i]

    if (compare(a, b)) {
      if (i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }

  list.length = ptr
  return list
}

function unique_eq (list) {
  var ptr = 1
  var len = list.length
  var a = list[0]
  var b = list[0]

  for (var i = 1; i < len; ++i) {
    b = a
    a = list[i]

    if (a !== b) {
      if (i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }

  list.length = ptr
  return list
}

module.exports = unique
<commit_msg>update array/uneek — add notes, check for null/undefined<commit_after>// uniq <https://github.com/mikolalysenko/uniq>
// Copyright (c) 2013 Mikola Lysenko (MIT)

'use strict'

/*

  Removes all duplicates from a sorted array in place.
    - array is the array to remove items from
    - compare is an optional comparison function that returns 0 when two items are equal, and something non-zero when they are different. If unspecified, then the default equals will be used.
    - sorted if true, then assume array is already sorted

  This library updates the array in place without making an extra copy (and so it is faster for large arrays)
  It also accepts a custom comparison function so you can remove duplicates from arrays containing object

*/

function unique (list, compare, sorted) {
  if (list == null || list.length === 0) return []

  if (compare) {
    if (!sorted) list.sort(compare)
    return unique_pred(list, compare)
  }

  if (!sorted) list.sort()
  return unique_eq(list)
}

function unique_pred (list, compare) {
  var ptr = 1
  var len = list.length
  var a = list[0]
  var b = list[0]

  for (var i = 1; i < len; ++i) {
    b = a
    a = list[i]

    if (compare(a, b)) {
      if (i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }

  list.length = ptr
  return list
}

function unique_eq (list) {
  var ptr = 1
  var len = list.length
  var a = list[0]
  var b = list[0]

  for (var i = 1; i < len; ++i) {
    b = a
    a = list[i]

    if (a !== b) {
      if (i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }

  list.length = ptr
  return list
}

module.exports = unique
<|endoftext|>