<commit_before>
import { SyncPromise as Promise } from 'sync-browser-mocks/src/promise';
import $logger from 'beaver-logger/client';
import xcomponent from 'xcomponent/src';

import parentTemplate from './parentTemplate.htm';
import componentTemplate from './componentTemplate.htm';

import { isDevice, request, getQueryParam } from '../../lib';
import { config } from '../../config';

import { validateProps, urlWillRedirectPage } from '../common';

import contentJSON from './content';
let content = JSON.parse(contentJSON);

function addHeader(name, value) {

    if (!window.$Api) {
        return;
    }

    if (window.$Api.addHeader) {
        return window.$Api.addHeader(name, value);
    }
}

function logReturnUrl(returnUrl) {

    let currentDomain = `${window.location.protocol}//${window.location.host}`.toLowerCase();
    returnUrl = returnUrl.toLowerCase();

    if (currentDomain !== 'https://www.paypal.com') {

        if (returnUrl.indexOf(currentDomain) === 0) {
            $logger.info(`return_url_domain_match`);
        } else {
            $logger.info(`return_url_domain_mismatch`, { returnUrl, currentDomain });
        }

        let currentHost = currentDomain.replace(/^https?/, '');
        let returnHost = returnUrl.replace(/^https?/, '');

        if (returnHost.indexOf(currentHost) === 0) {
            $logger.info(`return_url_host_match`);
        } else {
            $logger.info(`return_url_host_mismatch`, { returnUrl, currentDomain });
        }

        let currentTLD = currentHost.replace(/^www\./, '');
        let returnTLD = returnHost.replace(/^www\./, '');

        if (returnTLD.indexOf(currentTLD) === 0) {
            $logger.info(`return_url_tld_match`);
        } else {
            $logger.info(`return_url_tld_mismatch`, { returnUrl, currentDomain });
        }
    }
}

export let Checkout = xcomponent.create({

    tag: 'paypal-checkout',
    name: 'ppcheckout',

    buildUrl(instance, props) {
        let env = instance.props.env || config.env;

        if (instance.props.billingAgreement) {
            return config.billingUrls[env];
        }

        return props.payment().then(token => {

            if (token.indexOf('BA-') === 0) {
                $logger.info(`url_billing`);
                return config.billingUrls[env];
            }

            if (token.indexOf('PAY-') === 0) {
                $logger.info(`url_payment`);
                return config.checkoutUrls[env];
            }

            if (token.indexOf('EC-') === 0) {
                $logger.info(`url_checkout`);
                return config.checkoutUrls[env];
            }

            $logger.info(`url_default`);
            return config.checkoutUrls[env];
        });
    },

    remoteRenderDomain: config.paypal_domain_regex,

    get bridgeUrls() {
        return config.bridgeUrls;
    },

    contexts: {
        iframe: false,
        lightbox: false,
        popup: true
    },

    get version() {
        return config.ppobjects ? __FILE_VERSION__ : __MINOR_VERSION__;
    },

    get domains() {
        return config.paypalUrls;
    },

    validateProps(component, props, required = true) {
        if (required) {
            return validateProps(props);
        }
    },

    get componentTemplate() {

        return componentTemplate;
    },

    get parentTemplate() {

        let template = parentTemplate;
        let localeContent = content[config.locale.country][config.locale.lang];

        template = template.replace('#windowMessage', localeContent.windowMessage);
        template = template.replace('#continue', localeContent.continue);

        return template;
    },

    props: {

        env: {
            type: 'string',
            required: false,
            queryParam: true,

            def() {
                return config.env;
            }
        },

        stage: {
            type: 'string',
            required: false,
            queryParam: true,

            def() {
                return config.stage;
            }
        },

        locale: {
            type: 'string',
            required: false,
            queryParam: 'locale.x'
        },

        client: {
            type: 'object',
            required: false,
            def() {
                return {};
            },
            sendToChild: false
        },

        payment: {
            type: 'string',
            required: false,
            getter: true,
            memoize: true,
            queryParam(value = '') {
                return value.indexOf('BA-') === 0 ? 'ba_token' : 'token';
            },
            childDef() {
                return getQueryParam('token');
            },
            alias: 'paymentToken'
        },

        billingAgreement: {
            type: 'string',
            required: false,
            getter: true,
            memoize: true,
            queryParam: 'ba_token',
            alias: 'billingToken'
        },

        commit: {
            type: 'boolean',
            required: false
        },

        onAuthorize: {
            type: 'function',
            required: false,
            once: true,
            alias: 'onPaymentAuthorize',

            decorate(original) {
                if (original) {
                    return function(data, actions = {}) {

                        try {
                            logReturnUrl(data.returnUrl);
                        } catch (err) {
                            // pass
                        }

                        let close = () => {
                            return Promise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {

                            win = win || window.top;
                            url = url || data.returnUrl;

                            setTimeout(() => {
                                win.location = url;
                            }, 1);

                            return close().then(() => {
                                if (urlWillRedirectPage(url)) {
                                    return new Promise();
                                }
                            });
                        };

                        return Promise.try(() => {

                            try {
                                let isButton = window.location.href.indexOf('/webapps/hermes/button') !== -1;
                                let isGuest  = this.window.location.href.indexOf('/webapps/xoonboarding') !== -1;

                                if (isButton && isGuest) {
                                    return request({
                                        win: this.window,
                                        method: 'get',
                                        url: '/webapps/hermes/api/auth'
                                    }).then(result => {
                                        if (result && result.data && result.data.access_token) {
                                            addHeader('x-paypal-internal-euat', result.data.access_token);
                                        }
                                    }).catch(err2 => {
                                        // pass
                                    });
                                }

                            } catch (err) {
                                // pass
                            }

                        }).then(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).finally(() => {
                            return this.close();
                        });
                    };
                }
            }
        },

        onPaymentComplete: {
            type: 'function',
            required: false,
            sendToChild: false
        },

        onAuth: {
            type: 'function',
            required: false,
            sameDomain: true,
            decorate(original) {
                return function() {
                    enableCheckoutIframe();
                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        onCancel: {
            type: 'function',
            required: false,
            once: true,
            alias: 'onPaymentCancel',

            decorate(original) {
                if (original) {
                    return function(data, actions = {}) {

                        let close = () => {
                            return Promise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {

                            win = win || window.top;
                            url = url || data.cancelUrl;

                            setTimeout(() => {
                                win.location = url;
                            }, 1);

                            return close().then(() => {
                                if (urlWillRedirectPage(url)) {
                                    return new Promise();
                                }
                            });
                        };

                        return Promise.try(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).finally(() => {
                            this.close();
                        });
                    };
                }
            }
        },

        init: {
            type: 'function',
            required: false,
            once: true,

            def() {
                return function(data) {

                    this.paymentToken = data.paymentToken;
                    this.cancelUrl    = data.cancelUrl;
                };
            }
        },

        onClose: {
            type: 'function',
            required: false,
            once: true,
            promisify: true,

            def() {
                return function(reason) {
                    let CLOSE_REASONS = xcomponent.CONSTANTS.CLOSE_REASONS;

                    if (this.props.onCancel && this.paymentToken && this.cancelUrl && [ CLOSE_REASONS.CLOSE_DETECTED, CLOSE_REASONS.USER_CLOSED ].indexOf(reason) !== -1) {
                        return this.props.onCancel({
                            paymentToken: this.paymentToken,
                            cancelUrl:    this.cancelUrl
                        });
                    }
                };
            }
        },

        fallback: {
            type: 'function',
            required: false,
            once: true,

            def() {
                return function(url) {
                    $logger.warn('fallback', { url });

                    if (window.onLegacyPaymentAuthorize) {
                        window.onLegacyPaymentAuthorize(this.props.onAuthorize);
                    } else {
                        window.location = url;
                    }
                };
            }
        },

        testAction: {
            type: 'string',
            required: false,
            def() {
                return 'checkout';
            }
        }
    },

    autoResize: true,

    get dimensions() {

        if (isDevice()) {
            return {
                width: '100%',
                height: '100%'
            };
        }

        if (this.contexts.lightbox) {
            return {
                width: '450px',
                height: '300px'
            };
        }

        return {
            width: '450px',
            height: '535px'
        };
    }
});

let enableCheckoutIframeTimeout;

export function enableCheckoutIframe() {

    Checkout.contexts.lightbox = true;
    Checkout.contexts.iframe = true;

    if (enableCheckoutIframeTimeout) {
        clearTimeout(enableCheckoutIframeTimeout);
    }

    enableCheckoutIframeTimeout = setTimeout(() => {
        Checkout.contexts.lightbox = false;
        Checkout.contexts.iframe = false;
    }, 5 * 60 * 1000);
}
<commit_msg>Propagate errors up from checkout to button<commit_after>
import { SyncPromise as Promise } from 'sync-browser-mocks/src/promise';
import $logger from 'beaver-logger/client';
import xcomponent from 'xcomponent/src';

import parentTemplate from './parentTemplate.htm';
import componentTemplate from './componentTemplate.htm';

import { isDevice, request, getQueryParam } from '../../lib';
import { config } from '../../config';

import { validateProps, urlWillRedirectPage } from '../common';

import contentJSON from './content';
let content = JSON.parse(contentJSON);

function addHeader(name, value) {

    if (!window.$Api) {
        return;
    }

    if (window.$Api.addHeader) {
        return window.$Api.addHeader(name, value);
    }
}

function logReturnUrl(returnUrl) {

    let currentDomain = `${window.location.protocol}//${window.location.host}`.toLowerCase();
    returnUrl = returnUrl.toLowerCase();

    if (currentDomain !== 'https://www.paypal.com') {

        if (returnUrl.indexOf(currentDomain) === 0) {
            $logger.info(`return_url_domain_match`);
        } else {
            $logger.info(`return_url_domain_mismatch`, { returnUrl, currentDomain });
        }

        let currentHost = currentDomain.replace(/^https?/, '');
        let returnHost = returnUrl.replace(/^https?/, '');

        if (returnHost.indexOf(currentHost) === 0) {
            $logger.info(`return_url_host_match`);
        } else {
            $logger.info(`return_url_host_mismatch`, { returnUrl, currentDomain });
        }

        let currentTLD = currentHost.replace(/^www\./, '');
        let returnTLD = returnHost.replace(/^www\./, '');

        if (returnTLD.indexOf(currentTLD) === 0) {
            $logger.info(`return_url_tld_match`);
        } else {
            $logger.info(`return_url_tld_mismatch`, { returnUrl, currentDomain });
        }
    }
}

export let Checkout = xcomponent.create({

    tag: 'paypal-checkout',
    name: 'ppcheckout',

    buildUrl(instance, props) {
        let env = instance.props.env || config.env;

        if (instance.props.billingAgreement) {
            return config.billingUrls[env];
        }

        return props.payment().then(token => {

            if (token.indexOf('BA-') === 0) {
                $logger.info(`url_billing`);
                return config.billingUrls[env];
            }

            if (token.indexOf('PAY-') === 0) {
                $logger.info(`url_payment`);
                return config.checkoutUrls[env];
            }

            if (token.indexOf('EC-') === 0) {
                $logger.info(`url_checkout`);
                return config.checkoutUrls[env];
            }

            $logger.info(`url_default`);
            return config.checkoutUrls[env];
        });
    },

    remoteRenderDomain: config.paypal_domain_regex,

    get bridgeUrls() {
        return config.bridgeUrls;
    },

    contexts: {
        iframe: false,
        lightbox: false,
        popup: true
    },

    get version() {
        return config.ppobjects ? __FILE_VERSION__ : __MINOR_VERSION__;
    },

    get domains() {
        return config.paypalUrls;
    },

    validateProps(component, props, required = true) {
        if (required) {
            return validateProps(props);
        }
    },

    get componentTemplate() {

        return componentTemplate;
    },

    get parentTemplate() {

        let template = parentTemplate;
        let localeContent = content[config.locale.country][config.locale.lang];

        template = template.replace('#windowMessage', localeContent.windowMessage);
        template = template.replace('#continue', localeContent.continue);

        return template;
    },

    props: {

        env: {
            type: 'string',
            required: false,
            queryParam: true,

            def() {
                return config.env;
            }
        },

        stage: {
            type: 'string',
            required: false,
            queryParam: true,

            def() {
                return config.stage;
            }
        },

        locale: {
            type: 'string',
            required: false,
            queryParam: 'locale.x'
        },

        client: {
            type: 'object',
            required: false,
            def() {
                return {};
            },
            sendToChild: false
        },

        payment: {
            type: 'string',
            required: false,
            getter: true,
            memoize: true,
            queryParam(value = '') {
                return value.indexOf('BA-') === 0 ? 'ba_token' : 'token';
            },
            childDef() {
                return getQueryParam('token');
            },
            alias: 'paymentToken'
        },

        billingAgreement: {
            type: 'string',
            required: false,
            getter: true,
            memoize: true,
            queryParam: 'ba_token',
            alias: 'billingToken'
        },

        commit: {
            type: 'boolean',
            required: false
        },

        onAuthorize: {
            type: 'function',
            required: false,
            once: true,
            alias: 'onPaymentAuthorize',

            decorate(original) {
                if (original) {
                    return function(data, actions = {}) {

                        try {
                            logReturnUrl(data.returnUrl);
                        } catch (err) {
                            // pass
                        }

                        let close = () => {
                            return Promise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {

                            win = win || window.top;
                            url = url || data.returnUrl;

                            setTimeout(() => {
                                win.location = url;
                            }, 1);

                            return close().then(() => {
                                if (urlWillRedirectPage(url)) {
                                    return new Promise();
                                }
                            });
                        };

                        return Promise.try(() => {

                            try {
                                let isButton = window.location.href.indexOf('/webapps/hermes/button') !== -1;
                                let isGuest  = this.window.location.href.indexOf('/webapps/xoonboarding') !== -1;

                                if (isButton && isGuest) {
                                    return request({
                                        win: this.window,
                                        method: 'get',
                                        url: '/webapps/hermes/api/auth'
                                    }).then(result => {
                                        if (result && result.data && result.data.access_token) {
                                            addHeader('x-paypal-internal-euat', result.data.access_token);
                                        }
                                    }).catch(err2 => {
                                        // pass
                                    });
                                }

                            } catch (err) {
                                // pass
                            }

                        }).then(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).finally(() => {
                            return this.close();
                        });
                    };
                }
            }
        },

        onPaymentComplete: {
            type: 'function',
            required: false,
            sendToChild: false
        },

        onAuth: {
            type: 'function',
            required: false,
            sameDomain: true,
            decorate(original) {
                return function() {
                    enableCheckoutIframe();
                    if (original) {
                        return original.apply(this, arguments);
                    }
                };
            }
        },

        onCancel: {
            type: 'function',
            required: false,
            once: true,
            alias: 'onPaymentCancel',

            decorate(original) {
                if (original) {
                    return function(data, actions = {}) {

                        let close = () => {
                            return Promise.try(() => {
                                if (actions.close) {
                                    return actions.close();
                                }
                            }).then(() => {
                                return this.closeComponent();
                            });
                        };

                        let redirect = (win, url) => {

                            win = win || window.top;
                            url = url || data.cancelUrl;

                            setTimeout(() => {
                                win.location = url;
                            }, 1);

                            return close().then(() => {
                                if (urlWillRedirectPage(url)) {
                                    return new Promise();
                                }
                            });
                        };

                        return Promise.try(() => {
                            return original.call(this, data, { ...actions, close, redirect });
                        }).finally(() => {
                            this.close();
                        });
                    };
                }
            }
        },

        init: {
            type: 'function',
            required: false,
            once: true,

            def() {
                return function(data) {

                    this.paymentToken = data.paymentToken;
                    this.cancelUrl    = data.cancelUrl;
                };
            }
        },

        onClose: {
            type: 'function',
            required: false,
            once: true,
            promisify: true,

            def() {
                return function(reason) {
                    let CLOSE_REASONS = xcomponent.CONSTANTS.CLOSE_REASONS;

                    if (this.props.onCancel && this.paymentToken && this.cancelUrl && [ CLOSE_REASONS.CLOSE_DETECTED, CLOSE_REASONS.USER_CLOSED ].indexOf(reason) !== -1) {
                        return this.props.onCancel({
                            paymentToken: this.paymentToken,
                            cancelUrl:    this.cancelUrl
                        });
                    }
                };
            }
        },

        onError: {
            type: 'function',
            required: false,
            promisify: true,
            def() {
                return window.xprops && window.xprops.onError;
            },
            once: true
        },

        fallback: {
            type: 'function',
            required: false,
            once: true,

            def() {
                return function(url) {
                    $logger.warn('fallback', { url });

                    if (window.onLegacyPaymentAuthorize) {
                        window.onLegacyPaymentAuthorize(this.props.onAuthorize);
                    } else {
                        window.location = url;
                    }
                };
            }
        },

        testAction: {
            type: 'string',
            required: false,
            def() {
                return 'checkout';
            }
        }
    },

    autoResize: true,

    get dimensions() {

        if (isDevice()) {
            return {
                width: '100%',
                height: '100%'
            };
        }

        if (this.contexts.lightbox) {
            return {
                width: '450px',
                height: '300px'
            };
        }

        return {
            width: '450px',
            height: '535px'
        };
    }
});

let enableCheckoutIframeTimeout;

export function enableCheckoutIframe() {

    Checkout.contexts.lightbox = true;
    Checkout.contexts.iframe = true;

    if (enableCheckoutIframeTimeout) {
        clearTimeout(enableCheckoutIframeTimeout);
    }

    enableCheckoutIframeTimeout = setTimeout(() => {
        Checkout.contexts.lightbox = false;
        Checkout.contexts.iframe = false;
    }, 5 * 60 * 1000);
}
<|endoftext|>


<commit_before>import React, {
    PropTypes
} from 'react';
import {
    browserHistory
} from 'react-router'
import * as GifActions from '../../actions/GifActions';
import '../../css/GifAddModal.css';

class GifAddModal extends React.Component {
    static show() {
        $('#GifAddModal').modal('show');
    }
    static hide() {
        $('#GifAddModal').modal('hide');
    }
    constructor(props) {
        super(props);

        this.handleNameChange = this.handleNameChange.bind(this);
        this.handleUrlChange = this.handleUrlChange.bind(this);
        this.handleSave = this.handleSave.bind(this);

        this.state = {
            url: ''
        };
    }
    componentDidMount() {
        $('#GifAddModal').modal({
            show: false
        });
    }
    componentWillReceiveProps(nextProps) {
        this.setState({
            url: nextProps.url || '',
            still_url: nextProps.still_url
        });
    }
    handleNameChange(e) {
        this.setState({
            name: e.target.value
        });
    }
    handleUrlChange(e) {
        this.setState({
            url: e.target.value
        });
    }
    handleSave(e) {
        GifActions.create(this.state.url, this.state.name, this.state.still_url);
        GifAddModal.hide();
        browserHistory.push('/gifs');
    }
    render() {
        return (

                            <button type="button" className="close" data-dismiss="modal" aria-label="Close">

                                    ×

                            </button>

                                Add Gif

                            <form >

                                    <input type="text" className="form-control" name="name" placeholder="Name" autoComplete="off" onChange={this.handleNameChange}/>

                                    <input type="text" className="form-control" name="url" placeholder="Url" autoComplete="off" onChange={this.handleUrlChange} value={this.state.url} />

                            </form>

                            <button type="button" className="btn btn-danger" data-dismiss="modal">
                                Cancel
                            </button>
                            <button type="button" className="btn btn-success" onClick={this.handleSave}>
                                Save
                            </button>

        );
    }
};

GifAddModal.propTypes = {
    url: PropTypes.string,
    still_url: PropTypes.string
};

export default GifAddModal;<commit_msg>Add alert if field are missing<commit_after>import React, {
    PropTypes
} from 'react';
import {
    browserHistory
} from 'react-router';
import * as _ from 'lodash';
import * as GifActions from '../../actions/GifActions';
import '../../css/GifAddModal.css';

class GifAddModal extends React.Component {
    static show() {
        $('#GifAddModal').modal('show');
    }
    static hide() {
        $('#GifAddModal').modal('hide');
    }
    constructor(props) {
        super(props);

        this.handleNameChange = this.handleNameChange.bind(this);
        this.handleUrlChange = this.handleUrlChange.bind(this);
        this.handleSave = this.handleSave.bind(this);

        this.state = {
            url: ''
        };
    }
    componentDidMount() {
        $('#GifAddModal').modal({
            show: false
        });
    }
    componentWillReceiveProps(nextProps) {
        this.setState({
            url: nextProps.url || '',
            still_url: nextProps.still_url
        });
    }
    handleNameChange(e) {
        this.setState({
            name: e.target.value
        });
    }
    handleUrlChange(e) {
        this.setState({
            url: e.target.value
        });
    }
    handleSave(e) {
        let state = this.state,
            url = state.url,
            name = state.name,
            still_url = state.still_url;

        console.log(name, url);

        if (!_.isEmpty(url) && !_.isEmpty(name)) {
            GifActions.create(url, name, still_url);
            GifAddModal.hide();
            browserHistory.push('/gifs');
        } else {
            toastr.warning('Please provide a name and URL'); //eslint-disable-line no-undef
        }

    }
    render() {
        return (

                            <button type="button" className="close" data-dismiss="modal" aria-label="Close">

                                    ×

                            </button>

                                Add Gif

                            <form >

                                    <input type="text" className="form-control" name="name" placeholder="Name" autoComplete="off" onChange={this.handleNameChange}/>

                                    <input type="text" className="form-control" name="url" placeholder="Url" autoComplete="off" onChange={this.handleUrlChange} value={this.state.url} />

                            </form>

                            <button type="button" className="btn btn-danger" data-dismiss="modal">
                                Cancel
                            </button>
                            <button type="button" className="btn btn-success" onClick={this.handleSave}>
                                Save
                            </button>

        );
    }
};

GifAddModal.propTypes = {
    url: PropTypes.string,
    still_url: PropTypes.string
};

export default GifAddModal;<|endoftext|>


<commit_before>describe('Observer', function() {
	var object,
			observer,
			listenerSpy;

	beforeEach(function() {
		object = {};
		observer = new Observer(object);
		listenerSpy = jasmine.createSpy();
	});

	describe('watch()', function() {
		it('should watch object property', function() {
			observer.watch('hereIsSomeProperty', listenerSpy);

			object.hereIsSomeProperty = 0;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

			for(var i = 1; i < 10; i++) {
				object.hereIsSomeProperty = i;
				observer.deliverChangeRecords();

				expect(listenerSpy).toHaveBeenCalledWith(i, i - 1);
			}
		});

		it('should detect array indexes changes', function() {
			object.array = [
				[undefined, 0]
			];

			observer.watch('array.0.1', listenerSpy);
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

			object.array[0][1]++;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(1, 0);
		});
	});

	describe('deliverChangeRecords()', function() {
		it('should detect deep changes on properties', function() {
			observer.watch('detect.somePropertyChanges', listenerSpy);

			object.detect = {
				somePropertyChanges: 1
			};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(1, undefined);

			object.detect.somePropertyChanges++;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(2, 1);

			object.detect = {
				somePropertyChanges: {
					heyBuddy: {
						howAreYou: 'I am fine!'
					}
				}
			};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({
				heyBuddy: {
					howAreYou: 'I am fine!'
				}
			}, 2);
		});

		it('should detect collection changes', function() {
			observer.watch('myCollection', listenerSpy);

			object.myCollection = {};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({}, undefined);

			object.myCollection.youGotSomePropertyNow = 1;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({youGotSomePropertyNow: 1}, {});
		});

		it('should only call listeners when changes occur', function() {
			object.array = [1,2,3,4];
			observer.watch('array', listenerSpy);
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith([1,2,3,4], undefined);

			observer.deliverChangeRecords();

			expect(listenerSpy).not.toHaveBeenCalledWith([1,2,3,4], [1,2,3,4]);
		});
	});
});
<commit_msg>chore(test/Observer.js): New suite test to test Observer ability to detect changes on deep objects<commit_after>describe('Observer', function() {
	var object,
			observer,
			listenerSpy;

	beforeEach(function() {
		object = {};
		observer = new Observer(object);
		listenerSpy = jasmine.createSpy();
	});

	describe('watch()', function() {
		it('should watch object property', function() {
			observer.watch('hereIsSomeProperty', listenerSpy);

			object.hereIsSomeProperty = 0;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

			for(var i = 1; i < 10; i++) {
				object.hereIsSomeProperty = i;
				observer.deliverChangeRecords();

				expect(listenerSpy).toHaveBeenCalledWith(i, i - 1);
			}
		});

		it('should detect array indexes changes', function() {
			object.array = [
				[undefined, 0]
			];

			observer.watch('array.0.1', listenerSpy);
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

			object.array[0][1]++;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(1, 0);
		});

    it('should detect changes on objects', function(){
      var array = [];
      observer.watch('objectHere', listenerSpy);

      object.objectHere = {};
      observer.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith({}, undefined);

      object.objectHere.array = array;
      observer.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith({array:[]}, {});

      array.push(0);
      observer.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith({array:[0]}, {array:[]});
    });
	});

	describe('deliverChangeRecords()', function() {
		it('should detect deep changes on properties', function() {
			observer.watch('detect.somePropertyChanges', listenerSpy);

			object.detect = {
				somePropertyChanges: 1
			};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(1, undefined);

			object.detect.somePropertyChanges++;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith(2, 1);

			object.detect = {
				somePropertyChanges: {
					heyBuddy: {
						howAreYou: 'I am fine!'
					}
				}
			};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({
				heyBuddy: {
					howAreYou: 'I am fine!'
				}
			}, 2);
		});

		it('should detect collection changes', function() {
			observer.watch('myCollection', listenerSpy);

			object.myCollection = {};
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({}, undefined);

			object.myCollection.youGotSomePropertyNow = 1;
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith({youGotSomePropertyNow: 1}, {});
		});

		it('should only call listeners when changes occur', function() {
			object.array = [1,2,3,4];
			observer.watch('array', listenerSpy);
			observer.deliverChangeRecords();

			expect(listenerSpy).toHaveBeenCalledWith([1,2,3,4], undefined);

			observer.deliverChangeRecords();

			expect(listenerSpy).not.toHaveBeenCalledWith([1,2,3,4], [1,2,3,4]);
		});
	});
});
<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

const CheckboxGroup = ({ label, options }) => (

        {label
            ? {label}
            : null
        }
        {options.map((opt) =>

                    <input type="checkbox" value={opt.value} />
                    {opt.name}

        )}

);

export default CheckboxGroup;
<commit_msg>comment cleanup<commit_after>// dependencies
//
import React from 'react';
import uniqueId from 'lodash/uniqueId';

/*

        Cycles

                <input type="checkbox" value="1">
                1

                <input type="checkbox" value="2">
                2

*/

const CheckboxGroup = ({ label, options }) => (

        {label
            ? {label}
            : null
        }
        {options.map((opt) =>

                    <input type="checkbox" value={opt.value} />
                    {opt.name}

        )}

);

export default CheckboxGroup;
<|endoftext|>


<commit_before>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render, mount } from 'enzyme';
import chai from 'chai';
import sinon from 'sinon';

import Form from '../Form';

const expect = chai.expect;

/* eslint-disable no-unused-vars */
const debug = (component) => {
    console.info('------------------------------------------------------------');
    console.info(component.debug());
    console.info('------------------------------------------------------------');
};
/* eslint-enable no-unused-vars */

/* *****************************************************************************
a Form component containing a Dropdown
    should include a <Form.Dropdown> as a child
    should be a select.form-control
    should have the options specified in the `options` prop
    should have an empty set of options if options=[]
    should not include a label if none was specified
    should include a label if one was specified
    should select a specific value when value is set
    should select the first item when value is not set
    should select the first item when value is set to an invalid value
    should have a label.col-xs-3 when form is horizontal and labelColumns=3
    should have a div.col-xs-9 when form is horizontal and labelColumns=3

    should have the appropriate optgroups when options is an object
*/
describe('a Form component containing a Dropdown', () => {

    it('should include a <Form.Dropdown> as a child', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = shallow(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find(Form.Dropdown)).to.have.length(1);
    });

    it('should be a select.form-control', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('select.form-control')).to.have.length(1);
    });

    it('should have the options specified in the `options` prop', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('option')).to.have.length(options.length, 'options count');

        options.forEach((opt) => {
            expect(component.find(`option[value="${opt.value}"]`))
                .to.have.length(1, `value=${opt.value}`);
            expect(component.find(`option[value="${opt.value}"]`).text())
                .to.equal(opt.name, `name=${opt.name}`);

        });
    });

    it('should have an empty set of options if options=[]', () => {

        const options = [];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('option')).to.have.length(options.length, 'options count');
    });

    it('should not include a label if none was specified', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('label')).to.have.length(0);
    });

    it('should include a label if one was specified', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const label = 'some label';

        const component = render(
            <Form>
                <Form.Dropdown options={options} label={label} />
            </Form>
        );

        expect(component.find('label')).to.have.length(1);
        expect(component.find('label').text()).to.equal(label);
    });

    it('should select a specific value when value is set', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const value = '2';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={value} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(value);
    });

    it('should select the first item when value is not set', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const component = mount(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(options[0].value);
    });

    it('should select the first item when value is set to an invalid value', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const value = '9';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={value} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(options[0].value);
    });

    it('should have a label.col-xs-3 when form is horizontal and labelColumns=3', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const label = 'peter piper';
        const columns = 3;

        const value = 2;

        const component = mount(
            <Form horizontal labelColumns={columns}>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('div.form-horizontal')).to.have.length(1, 'form-horizontal');
        expect(component.find('label.control-label').props().className)
            .to.contain('col-xs-3', 'col-xs-3');
    });

    it('should have a div.col-xs-9 when form is horizontal and labelColumns=3', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const label = 'peter piper';
        const columns = 3;

        const value = 2;

        const component = mount(
            <Form horizontal labelColumns={columns}>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('div.form-horizontal')).to.have.length(1, 'form-horizontal');
        expect(component.find('div.form-dropdown-columns').props().className)
            .to.contain('col-xs-9', 'col-xs-9');
    });

    it('should have the appropriate optgroups when options is an object', () => {

        const options = {
            ['Group 1']: [
                { value: '1', name: 'one' },
                { value: '2', name: 'two' },
            ],
            ['Group 2']: [
                { value: '3', name: 'three' },
            ],
        };

        const label = 'peter piper';
        const value = 2;

        const component = mount(
            <Form>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('select optgroup')).to.have.length(2, 'optgroup');
        expect(component.find('select optgroup').at(0).text()).to.equal('Group 1', 'optgroup 1 text');
        expect(component.find('select optgroup').at(1).text()).to.equal('Group 2', 'optgroup 2 text');
        expect(component.find('select optgroup').at(0).find('option')).to.have.length(2, 'optgroup 1 options');
        expect(component.find('select optgroup').at(1).find('option')).to.have.length(1, 'optgroup 1 options');
    });
});

/* *****************************************************************************
the Dropdown element
    should call onChange with the correct value on value change to a valid value
    should call onChange with the first option on value change to a invalid value
*/
describe('the Dropdown element', () => {

    it('should call onChange with the correct value on value change to a valid value', () => {

        const onChange = sinon.spy();

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const initialValue = '2';

        const finalValue = '3';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={initialValue} onChange={onChange} />
            </Form>
        );

        component.find('select').simulate('change', {
            target: { value: finalValue }
        });

        expect(onChange.callCount).to.equal(1);
        expect(onChange.calledWith(finalValue));
    });

    it('should call onChange with the first option on value change to a invalid value', () => {

        const onChange = sinon.spy();

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const initialValue = '2';

        const finalValue = '9';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={initialValue} onChange={onChange} />
            </Form>
        );

        component.find('select').simulate('change', {
            target: { value: finalValue }
        });

        expect(onChange.callCount).to.equal(1);
        expect(onChange.args[0][0]).to.equal(options[0].value);
    });

});
<commit_msg>code cleanup<commit_after>// the eslint "no-unused-expressions" rule is triggered by the `expect`
// assertions in the tests. So, in order to avoid problems with eslint, I'm
// disabling that rule in this file
//
/* eslint-disable no-unused-expressions */

import React from 'react';
import { shallow, render, mount } from 'enzyme';
import chai from 'chai';
import sinon from 'sinon';

import Form from '../Form';

const expect = chai.expect;

/* eslint-disable no-unused-vars */
const debug = (component) => {
    console.info('------------------------------------------------------------');
    console.info(component.debug());
    console.info('------------------------------------------------------------');
};
/* eslint-enable no-unused-vars */

/* *****************************************************************************
a Form component containing a Dropdown
    should include a <Form.Dropdown> as a child
    should be a select.form-control
    should have the options specified in the `options` prop
    should have an empty set of options if options=[]
    should not include a label if none was specified
    should include a label if one was specified
    should select a specific value when value is set
    should select the first item when value is not set
    should select the first item when value is set to an invalid value
    should have a label.col-xs-3 when form is horizontal and labelColumns=3
    should have a div.col-xs-9 when form is horizontal and labelColumns=3

    should have the appropriate optgroups when options is an object
*/
describe('a Form component containing a Dropdown', () => {

    it('should include a <Form.Dropdown> as a child', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = shallow(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find(Form.Dropdown)).to.have.length(1);
    });

    it('should be a select.form-control', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('select.form-control')).to.have.length(1);
    });

    it('should have the options specified in the `options` prop', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('option')).to.have.length(options.length, 'options count');

        options.forEach((opt) => {
            expect(component.find(`option[value="${opt.value}"]`))
                .to.have.length(1, `value=${opt.value}`);
            expect(component.find(`option[value="${opt.value}"]`).text())
                .to.equal(opt.name, `name=${opt.name}`);

        });
    });

    it('should have an empty set of options if options=[]', () => {

        const options = [];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('option')).to.have.length(options.length, 'options count');
    });

    it('should not include a label if none was specified', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const component = render(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('label')).to.have.length(0);
    });

    it('should include a label if one was specified', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
        ];

        const label = 'some label';

        const component = render(
            <Form>
                <Form.Dropdown options={options} label={label} />
            </Form>
        );

        expect(component.find('label')).to.have.length(1);
        expect(component.find('label').text()).to.equal(label);
    });

    it('should select a specific value when value is set', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const value = '2';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={value} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(value);
    });

    it('should select the first item when value is not set', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const component = mount(
            <Form>
                <Form.Dropdown options={options} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(options[0].value);
    });

    it('should select the first item when value is set to an invalid value', () => {
        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const value = '9';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={value} />
            </Form>
        );

        expect(component.find('select').props().value).to.equal(options[0].value);
    });

    it('should have a label.col-xs-3 when form is horizontal and labelColumns=3', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const label = 'peter piper';
        const columns = 3;

        const value = 2;

        const component = mount(
            <Form horizontal labelColumns={columns}>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('div.form-horizontal')).to.have.length(1, 'form-horizontal');
        expect(component.find('label.control-label').props().className)
            .to.contain('col-xs-3', 'col-xs-3');
    });

    it('should have a div.col-xs-9 when form is horizontal and labelColumns=3', () => {

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const label = 'peter piper';
        const columns = 3;

        const value = 2;

        const component = mount(
            <Form horizontal labelColumns={columns}>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('div.form-horizontal')).to.have.length(1, 'form-horizontal');
        expect(component.find('div.form-dropdown-columns').props().className)
            .to.contain('col-xs-9', 'col-xs-9');
    });

    it('should have the appropriate optgroups when options is an object', () => {

        const options = {
            'Group 1': [
                { value: '1', name: 'one' },
                { value: '2', name: 'two' },
            ],
            'Group 2': [
                { value: '3', name: 'three' },
            ],
        };

        const label = 'peter piper';
        const value = 2;

        const component = mount(
            <Form>
                <Form.Dropdown label={label} options={options} value={value} />
            </Form>
        );

        expect(component.find('select optgroup')).to.have.length(2, 'optgroup');
        expect(component.find('select optgroup').at(0).props().label)
            .to.equal('Group 1', 'optgroup 1 text');
        expect(component.find('select optgroup').at(1).props().label)
            .to.equal('Group 2', 'optgroup 2 text');
        expect(component.find('select optgroup').at(0).find('option'))
            .to.have.length(2, 'optgroup 1 options');
        expect(component.find('select optgroup').at(1).find('option'))
            .to.have.length(1, 'optgroup 1 options');
    });
});

/* *****************************************************************************
the Dropdown element
    should call onChange with the correct value on value change to a valid value
    should call onChange with the first option on value change to a invalid value
*/
describe('the Dropdown element', () => {

    it('should call onChange with the correct value on value change to a valid value', () => {

        const onChange = sinon.spy();

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const initialValue = '2';

        const finalValue = '3';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={initialValue} onChange={onChange} />
            </Form>
        );

        component.find('select').simulate('change', {
            target: { value: finalValue }
        });

        expect(onChange.callCount).to.equal(1);
        expect(onChange.calledWith(finalValue));
    });

    it('should call onChange with the first option on value change to a invalid value', () => {

        const onChange = sinon.spy();

        const options = [
            { value: '1', name: 'one' },
            { value: '2', name: 'two' },
            { value: '3', name: 'three' },
        ];

        const initialValue = '2';

        const finalValue = '9';

        const component = mount(
            <Form>
                <Form.Dropdown options={options} value={initialValue} onChange={onChange} />
            </Form>
        );

        component.find('select').simulate('change', {
            target: { value: finalValue }
        });

        expect(onChange.callCount).to.equal(1);
        expect(onChange.args[0][0]).to.equal(options[0].value);
    });

});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import FormControl from './FormControl'

/* eslint-disable react/prefer-stateless-function */
class EmailControl extends Component {

  static defaultProps = {
    className: 'EmailControl',
    id: 'email',
    name: 'user[email]',
    placeholder: 'Enter your email',
  }

  render() {
    return (
      <FormControl
        { ...this.props }
        autoCapitalize="off"
        autoCorrect="off"
        type="email"
        ref=""
      />
    )
  }
}

export default EmailControl

<commit_msg>this is the value<commit_after>import React, { Component } from 'react'
import FormControl from './FormControl'

/* eslint-disable react/prefer-stateless-function */
class EmailControl extends Component {

  static defaultProps = {
    className: 'EmailControl',
    id: 'email',
    name: 'user[email]',
    placeholder: 'Enter your email',
  }

  render() {
    return (
      <FormControl
        { ...this.props }
        autoCapitalize="off"
        autoCorrect="off"
        type="email"
        ref="formControl"
      />
    )
  }
}

export default EmailControl

<|endoftext|>


<commit_before>define(['jquery', 'sdcard'], function ($) {

  var updateStatus = function(status) {
      $("#backup-status").html(status);
  };

  var saveContactsToSdcard = function(contacts) {

    // Do not accept empty contact list
    if (contacts.length <= 0)
      return;

    if (SdCard.checkStorageCard()) {

    }

  };

  $(document).ready(function () {

    $(".btn-done").click(function() {

      var cursor = navigator.mozContacts.getAll({});
      var contacts = [];

      cursor.onsuccess = function onsuccess(evt) {

        var contact = evt.target.result;

        if (contact) {

          contacts.push(contact);
          cursor.continue();

        } else {

          if (contacts.length > 0) {
            updateStatus(contacts.length + " contacts");
            saveContactsToSdcard(contacts);
          } else {
            updateStatus("No contact found.");
          }

        }
      };

    });

  });

});
<commit_msg>Add scard writing patch from Jason Weathersby<commit_after>define(['jquery', 'sdcard'], function ($) {

  var updateStatus = function(status) {
      $("#backup-status").html(status);
  };

  var saveContactsToSdcard = function(contacts) {

    // Do not accept empty contact list
    if (contacts.length <= 0)
      return;

var vcard = "BEGIN:VCARD"+"\n"+
"VERSION:4.0"+"\n"+
"N:Gump;Forrest;;;"+"\n"+
"FN:Forrest Gump"+"\n"+
"ORG:Bubba Gump Shrimp Co."+"\n"+
"TITLE:Shrimp Man"+"\n"+
"PHOTO;MEDIATYPE=image/gif:http://www.example.com/dir_photos/my_photo.gif"+"\n"+
"TEL;TYPE=work,voice;VALUE=uri:tel:+1-111-555-1212"+"\n"+
"TEL;TYPE=home,voice;VALUE=uri:tel:+1-404-555-1212"+"\n"+
"ADR;TYPE=work;LABEL='100 Waters Edge\nBaytown, LA 30314\nUnited States of America'"+"\n"+
" :;;100 Waters Edge;Baytown;LA;30314;United States of America"+"\n"+
"ADR;TYPE=home;LABEL='42 Plantation St.\nBaytown, LA 30314\nUnited States of America'"+"\n"+
" :;;42 Plantation St.;Baytown;LA;30314;United States of America"+"\n"+
"EMAIL:forrestgump@example.com"+"\n"+
"REV:20080424T195243Z"+"\n"+
"END:VCARD";
var sdcard = navigator.getDeviceStorage("sdcard");
var file = new Blob([vcard], {type: "text/plain"});
var request = sdcard.addNamed(file, "test/my-file.vcf");
request.onsuccess = function () {
var name = this.result;
alert('File "' + name + '" successfully wrote on the sdcard storage area');
}
// An error typically occur if a file with the same name already exist
request.onerror = function () {
alert('Unable to write the file: ' + this.error.name);
}

  };

  $(document).ready(function () {

    $(".btn-done").click(function() {

      var cursor = navigator.mozContacts.getAll({});
      var contacts = [];

      cursor.onsuccess = function onsuccess(evt) {

        var contact = evt.target.result;

        if (contact) {

          contacts.push(contact);
          cursor.continue();

        } else {

          if (contacts.length > 0) {
            updateStatus(contacts.length + " contacts");
            saveContactsToSdcard(contacts);
          } else {
            updateStatus("No contact found.");
          }

        }
      };

    });

  });

});
<|endoftext|>


<commit_before>var MIM = angular.module('starter.services', []);

MIM.factory('DB', function($q, $ionicPlatform, $cordovaSQLite) {
  this.queryStatement = function(query, parameters) {
    parameters = parameters || [];
    var q = $q.defer();

    $ionicPlatform.ready(function() {
      $cordovaSQLite.execute(db, query, parameters).then(function(res) {
        q.resolve(res);
      }, function(error) {
        console.error(error);
        q.reject(error);
      });
    });

    return q.promise;
  };
});
<commit_msg>Add global query function getAll and getById<commit_after>var MIM = angular.module('starter.services', []);

MIM.factory('DB', function($q, $ionicPlatform, $cordovaSQLite) {
  this.queryStatement = function(query, parameters) {
    parameters = parameters || [];
    var q = $q.defer();

    $ionicPlatform.ready(function() {
      $cordovaSQLite.execute(db, query, parameters).then(function(res) {
        q.resolve(res);
      }, function(error) {
        console.error(error);
        q.reject(error);
      });
    });

    return q.promise;
  };

  this.getAll = function(res) {
    var output = [];

    if (res.rows.length) {
      for (var i = 0; i < res.rows.length; i++) {
        output.push(res.rows.item(i));
      }
    }

    return output;
  };

  this.getById = function(res) {
    var output = null;

    if (res.rows.length) {
      angular.copy(output, res.rows.item(0));
    }

    return output;
  }

  return this;
});
<|endoftext|>


<commit_before>angular.module('pvta.services', ['ngResource'])

.factory('Vehicle', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/vehicles/get/:vehicleId');
})

.factory('Route', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/routedetails/get/:routeId');
})

.factory('Routes', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/routes/getvisibleroutes');
})

.factory('Stop', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/stops/get/:stopId');
})

.factory('Stops', function($resource){
    return $resource('http://bustracker.pvta.com/infopoint/rest/stops/getallstops');
})

.factory('RouteVehicles', function($resource){
    return $resource('http://bustracker.pvta.com/infopoint/rest/vehicles/getallvehiclesforroute?routeId=:routeId')
})

.factory('StopDeparture', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/stopdepartures/get/:stopId');
})

.factory('Messages', function ($resource) {
  return $resource('http://bustracker.pvta.com/infopoint/rest/publicmessages/getcurrentmessages');
})

.factory('StopList', function(){
  var stopsList = [];
  var pushToList = function(stop){
    stopsList.push(stop);
  };
  var pushEntireList = function(list){
    stopsList.concat(list);
    return stopsList;
  };

  var getEntireList = function(){
    if(stopsList !== undefined){
      return stopsList;
    }
    else return 0;
  }

  var isEmpty = function(){
    if(stopsList.length === 0) return true;
    else return false
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    pushToList: pushToList,
    isEmpty: isEmpty,
  };

})

.factory('RouteList', function(){
  var routesList = [];

  var pushToList = function(route){
    routesList.push(route);
  };
  var pushEntireList = function(list){
   routesList = list;
   return routesList;
  };

  var getEntireList = function(){
    if(!isEmpty()) {
      return routesList;
    }
    else return 0;
  }

  var isEmpty = function(){
    if(routesList.length === 0) return true;
    else return false
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    pushToList: pushToList,
    isEmpty: isEmpty,
  };

})

.factory('FavoriteRoutes', function(){
  var routes = [];
  var push = function(route){
    localforage.getItem('favoriteRoutes', function(err, value){
      var newArray = [];
      if(value !== null) {
        newArray = value;
        newArray.push(route);
      }
      else{
        newArray.push(route);
      }
      localforage.setItem('favoriteRoutes', newArray, function(err, value){
      })
    })
  };

  var getAll = function(){
    var ret = [];
    localforage.getItem('favoriteRoutes', function(err, value){

    })
  };

  var remove = function(route){
    localforage.getItem('favoriteRoutes', function(err, routes){
      for(var i = 0; i < routes.length; i++){
        if(routes[i].RouteId === route.RouteId) {
          routes.splice(i, 1);
        }
      }
      localforage.setItem('favoriteRoutes', routes, function(err, newRoutes){
      });
    });
    removeOneRoute(route);
  };

  var removeOneRoute = function(route){
    var name = 'Route ' + route.ShortName + ' favorite';
    localforage.removeItem(name, function(err){
      if(err) console.log(err);
    });
  };

  return{
    push: push,
    getAll: getAll,
    remove: remove
  };
})

.factory('FavoriteStops', function(){
  var stops = [];
  var push = function(stop){
    localforage.getItem('favoriteStops', function(err, value){
      var newArray = [];
      if(value !== null) {
        newArray = value;
        newArray.push(stop);
      }
      else{
        newArray.push(stop);
      }
      localforage.setItem('favoriteStops', newArray, function(err, value){
      });
    });
  };

  var getAll = function(){
    var ret = [];
    localforage.getItem('favoriteStops', function(err, value){
    });
  };

  var remove = function(stop){
    localforage.getItem('favoriteStops', function(err, stops){
      for(var i = 0; i < stops.length; i++){
        if(stops[i].StopId === stop.StopId) {
          stops.splice(i, 1);
        }
      }
      localforage.setItem('favoriteStops', stops, function(err, newStops){
      });
    });

    //Since stops also have their own separate entries,
    // (for toggling the heart on the Stop's page),
    // remove that too.
    removeOneStop(stop);
  };

  var removeOneStop = function(stop){
    var name = 'Stop ' + stop.Name + " favorite";
    localforage.removeItem(name, function(err){
      if(err) console.log(err);
    });
  }

  return{
    push: push,
    getAll: getAll,
    remove: remove
  };
})

.factory('MyLocation', function($cordovaGeolocation){
  var userLocation;

  calculateLocation = function(){
    $cordovaGeolocation.getCurrentPosition().then(function(position){
      userLocation = position.coords;
    });
  }

  getDistanceFrom = function(lat2, lon2) {
    lat1 = userLocation.latitude;
    lon1 = userLocation.longitude;
    var radlat1 = Math.PI * lat1/180
    var radlat2 = Math.PI * lat2/180
    var radlon1 = Math.PI * lon1/180
    var radlon2 = Math.PI * lon2/180
    var theta = lon1-lon2
    var radtheta = Math.PI * theta/180
    var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
    dist = Math.acos(dist)
    dist = dist * 180/Math.PI
    dist = dist * 60 * 1.1515
    return dist
  }
  return{
    userLocation: userLocation,
    calculateLocation: calculateLocation,
    getDistanceFrom: getDistanceFrom
  };
})

.service('LatLong', function(){
  var latlong = [];
  return {
    push: function(lat, long){
      var p = {lat, long};
      latlong.push(p);
    },
    pop: function(){
      var x = latlong.pop();
      return x;
    }
  };
});

function distance(lat1, lon1, lat2, lon2) {
  var radlat1 = Math.PI * lat1/180
  var radlat2 = Math.PI * lat2/180
  var radlon1 = Math.PI * lon1/180
  var radlon2 = Math.PI * lon2/180
  var theta = lon1-lon2
  var radtheta = Math.PI * theta/180
  var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
  dist = Math.acos(dist)
  dist = dist * 180/Math.PI
  dist = dist * 60 * 1.1515
  return dist
}
<commit_msg>removed redundant method<commit_after>angular.module('pvta.services', ['ngResource'])

.factory('Vehicle', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/vehicles/get/:vehicleId');
})

.factory('Route', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/routedetails/get/:routeId');
})

.factory('Routes', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/routes/getvisibleroutes');
})

.factory('Stop', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/stops/get/:stopId');
})

.factory('Stops', function($resource){
    return $resource('http://bustracker.pvta.com/infopoint/rest/stops/getallstops');
})

.factory('RouteVehicles', function($resource){
    return $resource('http://bustracker.pvta.com/infopoint/rest/vehicles/getallvehiclesforroute?routeId=:routeId')
})

.factory('StopDeparture', function ($resource) {
    return $resource('http://bustracker.pvta.com/infopoint/rest/stopdepartures/get/:stopId');
})

.factory('Messages', function ($resource) {
  return $resource('http://bustracker.pvta.com/infopoint/rest/publicmessages/getcurrentmessages');
})

.factory('StopList', function(){
  var stopsList = [];
  var pushToList = function(stop){
    stopsList.push(stop);
  };
  var pushEntireList = function(list){
    stopsList.concat(list);
    return stopsList;
  };

  var getEntireList = function(){
    if(stopsList !== undefined){
      return stopsList;
    }
    else return 0;
  }

  var isEmpty = function(){
    if(stopsList.length === 0) return true;
    else return false
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    pushToList: pushToList,
    isEmpty: isEmpty,
  };

})

.factory('RouteList', function(){
  var routesList = [];

  var pushToList = function(route){
    routesList.push(route);
  };
  var pushEntireList = function(list){
   routesList = list;
   return routesList;
  };

  var getEntireList = function(){
    if(!isEmpty()) {
      return routesList;
    }
    else return 0;
  }

  var isEmpty = function(){
    if(routesList.length === 0) return true;
    else return false
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    pushToList: pushToList,
    isEmpty: isEmpty,
  };

})

.factory('FavoriteRoutes', function(){
  var routes = [];
  var push = function(route){
    localforage.getItem('favoriteRoutes', function(err, value){
      var newArray = [];
      if(value !== null) {
        newArray = value;
        newArray.push(route);
      }
      else{
        newArray.push(route);
      }
      localforage.setItem('favoriteRoutes', newArray, function(err, value){
      })
    })
  };

  var getAll = function(){
    var ret = [];
    localforage.getItem('favoriteRoutes', function(err, value){

    })
  };

  var remove = function(route){
    localforage.getItem('favoriteRoutes', function(err, routes){
      for(var i = 0; i < routes.length; i++){
        if(routes[i].RouteId === route.RouteId) {
          routes.splice(i, 1);
        }
      }
      localforage.setItem('favoriteRoutes', routes, function(err, newRoutes){
      });
    });
    removeOneRoute(route);
  };

  var removeOneRoute = function(route){
    var name = 'Route ' + route.ShortName + ' favorite';
    localforage.removeItem(name, function(err){
      if(err) console.log(err);
    });
  };

  return{
    push: push,
    getAll: getAll,
    remove: remove
  };
})

.factory('FavoriteStops', function(){
  var stops = [];
  var push = function(stop){
    localforage.getItem('favoriteStops', function(err, value){
      var newArray = [];
      if(value !== null) {
        newArray = value;
        newArray.push(stop);
      }
      else{
        newArray.push(stop);
      }
      localforage.setItem('favoriteStops', newArray, function(err, value){
      });
    });
  };

  var getAll = function(){
    var ret = [];
    localforage.getItem('favoriteStops', function(err, value){
    });
  };

  var remove = function(stop){
    localforage.getItem('favoriteStops', function(err, stops){
      for(var i = 0; i < stops.length; i++){
        if(stops[i].StopId === stop.StopId) {
          stops.splice(i, 1);
        }
      }
      localforage.setItem('favoriteStops', stops, function(err, newStops){
      });
    });

    //Since stops also have their own separate entries,
    // (for toggling the heart on the Stop's page),
    // remove that too.
    removeOneStop(stop);
  };

  var removeOneStop = function(stop){
    var name = 'Stop ' + stop.Name + " favorite";
    localforage.removeItem(name, function(err){
      if(err) console.log(err);
    });
  }

  return{
    push: push,
    getAll: getAll,
    remove: remove
  };
})

.factory('MyLocation', function($cordovaGeolocation){
  var userLocation;

  calculateLocation = function(){
    $cordovaGeolocation.getCurrentPosition().then(function(position){
      userLocation = position.coords;
    });
  }

  getDistanceFrom = function(lat2, lon2) {
    lat1 = userLocation.latitude;
    lon1 = userLocation.longitude;
    var radlat1 = Math.PI * lat1/180
    var radlat2 = Math.PI * lat2/180
    var radlon1 = Math.PI * lon1/180
    var radlon2 = Math.PI * lon2/180
    var theta = lon1-lon2
    var radtheta = Math.PI * theta/180
    var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
    dist = Math.acos(dist)
    dist = dist * 180/Math.PI
    dist = dist * 60 * 1.1515
    return dist
  }
  return{
    userLocation: userLocation,
    calculateLocation: calculateLocation,
    getDistanceFrom: getDistanceFrom
  };
})

.service('LatLong', function(){
  var latlong = [];
  return {
    push: function(lat, long){
      var p = {lat, long};
      latlong.push(p);
    },
    pop: function(){
      var x = latlong.pop();
      return x;
    }
  };
});
<|endoftext|>


<commit_before>angular.module('starter.services', [])

.controller('AlertCenterCtrl', ['$http', function($http){
  var alertCenter = this;
  alertCenter.notificationList = [];
  // alertCenter.notificationList = [{date:"2014-09-14T00:50:30.048Z"},{date:"2014-09-14T00:40:30.048Z"}];
  notificationList_dateISOString_toDateAgo(alertCenter.notificationList);
  $http.get("http://buddhabrudda.mybluemix.net/notifications/4699553379")
  .success(function(data){
    alertCenter.notificationList = data;
    // alertCenter.notificationList = [{time:"2014-09-14T00:50:30.048Z"},{time:"2014-09-14T00:40:30.048Z"}];
    notificationList_dateISOString_toDateAgo(alertCenter.notificationList);
  });
}]);

function notificationList_dateISOString_toDateAgo(notificationList){
  for (var i=0;i<notificationList.length; ++i){
    var dateISOString = notificationList[i].date;
    notificationList[i].ago = moment(dateISOString).fromNow();
  }
  return notificationList;
}
<commit_msg>added polling - 3 sec<commit_after>angular.module('starter.services', [])

.controller('AlertCenterCtrl', ['$http', function($http){
  var alertCenter = this;
  alertCenter.notificationList = [];
  // alertCenter.notificationList = [{date:"2014-09-14T00:50:30.048Z"},{date:"2014-09-14T00:40:30.048Z"}];
  notificationList_dateISOString_toDateAgo(alertCenter.notificationList);
  setInterval(function(){
    $http.get("http://buddhabrudda.mybluemix.net/notifications/4699553379")
    .success(function(data){
      alertCenter.notificationList = data;
      // alertCenter.notificationList = [{time:"2014-09-14T00:50:30.048Z"},{time:"2014-09-14T00:40:30.048Z"}];
      notificationList_dateISOString_toDateAgo(alertCenter.notificationList);
    });
  }, 3000);
}]);

function notificationList_dateISOString_toDateAgo(notificationList){
  for (var i=0;i<notificationList.length; ++i){
    var dateISOString = notificationList[i].date;
    notificationList[i].ago = moment(dateISOString).fromNow();
  }
  return notificationList;
}
<|endoftext|>


<commit_before>
var View = function View(options) {
    'use strict';
    // Set options to an empty object if passed options is empty
    if (typeof options !== 'object') {
        options = {};
    }
    // Create a new instance if object is not a instance of Base
    if (!(this instanceof View)) {
        return new View(options);
    }
    // Defined pre-default values
    this.attributes = {};
    // Set options to `this.attributes` object
    for (var i in options) {
        this.attributes[i] = options[i];
    }
    // Set debugging mode
    this.set('debugging', true);
    // Set intialized status
    this.set('initialized', false);
    // Patch view
    if (typeof this.patch === 'function') {
        this.patch();
    }
    // Return self object
    if (typeof this.controller === 'function') {
        this.controller();
    }
    return this;
};

View.EVENT = {
    ATTRIBUTE_CHANGED: 'change',
    ATTRIBUTE_DELETED: 'delete',
    ATTRIBUTE_CREATED: 'create'
};

View.prototype.constructor = View;

View.prototype.id = function() {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof this.get('_id') !== 'string' || (typeof this.get('_id') && this.get('_id').trim().length === 0)) {
        this.set('_id', Math.random().toString(36).substring(7));
    }
    return this.get('_id');
};

View.prototype.self = View.prototype.instance = function() {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    return this;
};

View.prototype.get = function(key) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        return this.attributes[key];
    }
    return this.attributes;
};

View.prototype.set = function(key, value) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        this.attributes[key] = value;
        // this.emit(
    }
    return this;
};

View.prototype.unset = function(key) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        delete this.attributes[key];
    }
    return this;
};

View.prototype.append = function(key, value) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string' && typeof value === 'string') {
        if (typeof this.attributes[key] !== 'string') {
            this.attributes[key] = '';
        }
        this.attributes[key] += value;
    } else if (typeof key === 'string' && typeof value === 'object' && value instanceof Array) {
        if (!(typeof this.attributes[key] === 'object' && this.attributes[key] instanceof Array)) {
            this.attributes[key] = [];
        }
        Array.prototype.push.apply(this.attributes[key], value);
    }
    return this.attributes[key];
};

View.prototype.on = function(event, cb) {
    require('electron').ipcRenderer.on(event, cb.bind(this));
};

View.prototype.off = View.prototype.removeListener = function(event, cb) {
    require('electron').ipcRenderer.removeListener(event, cb.bind(this));
};

View.prototype.emit = function(event) {
    var ipc = require('electron').ipcRenderer;
    if (typeof event !== 'string') {
        this.throw('missing event name');
    }
    ipc.send.apply(ipc, arguments);
};

View.prototype.send = View.prototype.emit;

View.prototype.log = function(str) {
    if (this.get('debugging') === true) {
        var args;
        if ((typeof str === 'string' && str.indexOf('{0}') == -1) || (typeof str !== 'string')) {
            args = ['[' + this.constructor.name + ']'];
            for (var i = 0; i < arguments.length; i++) {
                args.push(arguments[i]);
            }
            console.log(args.join(' '));
        } else if (typeof str === 'string' && str.indexOf('{0}') > -1) {
            args = arguments;
            str = str.replace(/\{([0-9]+)\}/g, function (match, index) {
                return args[parseInt(index) + 1];
            });
            console.log('[' + this.constructor.name + ']', str);
        }
    }
    return this;
};

View.prototype.throw = function(str) {
    var args;
    if ((typeof str === 'string' && str.indexOf('{0}') == -1) || (typeof str !== 'string')) {
        args = ['[' + this.constructor.name + ']'];
        for (var i = 0; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        throw args.join(' ');
    } else if (typeof str === 'string' && str.indexOf('{0}') > -1) {
        args = arguments;
        str = str.replace(/\{([0-9]+)\}/g, function (match, index) {
            return args[parseInt(index) + 1];
        });
        throw ('[' + this.constructor.name + ']' + str);
    }
};

View.prototype.component = function(options) {
    if (typeof options !== 'object') {
        options = {};
    }
    for (var i in options) {
        this.attributes[i] = options[i];
    }
};

View.prototype.patch = function() {
    // Patch targets
    var targets = [ 'controller', 'view' ],
        prototypes = Object.keys(View.prototype);
    for (var i = 0; i < targets.length; i++) {
        var target = targets[i];
        if (typeof this[target] === 'function') {
            for (var j = 0; j < prototypes.length; j++) {
                this[target].prototype[prototypes[j]] = this[prototypes[j]].bind(this);
            }
        }
    }
};

View.prototype.stylesheet = function(list) {
    var args = [];
    if (list instanceof Array) {
        args = list;
    } else {
        args = arguments;
    }
    for (var i = 0; i < args.length; i++) {
        // Get stylesheet path
        var path  = [ '..', 'styles', 'dist', args[i] ].join('/'),
            links = document.head.querySelectorAll('link[href="' + path + '"]');
        // Only link css if stylesheet is not present in the current html
        if (links.length === 0) {
            // Generate a link element
            var link = document.createElement('link');
            link.setAttribute('href', path);
            link.setAttribute('rel', 'stylesheet');
            // Append the stylesheet to html head
            document.head.appendChild(link);
        }
    }
};

View.prototype.autoconfig = function(element, isInit, context) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof this.unload === 'function') {
        context.onunload = this.unload.call(this);
    }
};

View.prototype.unload = function() {
    this.set('initialized', false);
};

App.Common.View = View;

<commit_msg>extend view class with EventEmitter<commit_after>
var View = function View(options) {
    'use strict';
    // Set options to an empty object if passed options is empty
    if (typeof options !== 'object') {
        options = {};
    }
    // Create a new instance if object is not a instance of Base
    if (!(this instanceof View)) {
        return new View(options);
    }
    // Defined pre-default values
    this.attributes = {};
    // Set options to `this.attributes` object
    for (var i in options) {
        this.attributes[i] = options[i];
    }
    // Set debugging mode
    this.set('debugging', true);
    // Set intialized status
    this.set('initialized', false);
    // Patch view
    if (typeof this.patch === 'function') {
        this.patch();
    }
    // Return self object
    if (typeof this.controller === 'function') {
        this.controller();
    }
    EventEmitter.call(this);
    return this;
};

View.EVENT = {
    ATTRIBUTE_CHANGED: 'change',
    ATTRIBUTE_DELETED: 'delete',
    ATTRIBUTE_CREATED: 'create'
};

View.prototype = Object.create(EventEmitter.prototype);
View.prototype.constructor = View;

View.prototype.id = function() {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof this.get('_id') !== 'string' || (typeof this.get('_id') && this.get('_id').trim().length === 0)) {
        this.set('_id', Math.random().toString(36).substring(7));
    }
    return this.get('_id');
};

View.prototype.self = View.prototype.instance = function() {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    return this;
};

View.prototype.get = function(key) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        return this.attributes[key];
    }
    return this.attributes;
};

View.prototype.set = function(key, value) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        this.attributes[key] = value;
        this.instance().emit(View.EVENT.ATTRIBUTE_CREATED, key, this.attributes[key]);
    }
    return this;
};

View.prototype.unset = function(key) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof key === 'string') {
        delete this.attributes[key];
    }
    this.instance().emit(View.EVENT.ATTRIBUTE_DELETED, key, this.attributes[key]);
    return this;
};

View.prototype.append = function(key, value) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    var previous;
    if (typeof key === 'string' && typeof value === 'string') {
        if (typeof this.attributes[key] !== 'string') {
            this.attributes[key] = '';
        }
        this.attributes[key] += value;
        this.instance().emit(View.EVENT.ATTRIBUTE_CHANGED, key, this.attributes[key]);
    } else if (typeof key === 'string' && typeof value === 'object' && value instanceof Array) {
        if (!(typeof this.attributes[key] === 'object' && this.attributes[key] instanceof Array)) {
            this.attributes[key] = [];
        }
        Array.prototype.push.apply(this.attributes[key], value);
        this.instance().emit(View.EVENT.ATTRIBUTE_CHANGED, key, this.attributes[key]);
    }
    return this.attributes[key];
};

View.prototype.register = function(event, cb) {
    require('electron').ipcRenderer.on(event, cb.bind(this));
};

View.prototype.unregister = View.prototype.removeListener = function(event, cb) {
    require('electron').ipcRenderer.removeListener(event, cb.bind(this));
};

View.prototype.send = function(event) {
    var ipc = require('electron').ipcRenderer;
    if (typeof event !== 'string') {
        this.throw('missing event name');
    }
    ipc.send.apply(ipc, arguments);
};

View.prototype.log = function(str) {
    if (this.get('debugging') === true) {
        var args;
        if ((typeof str === 'string' && str.indexOf('{0}') == -1) || (typeof str !== 'string')) {
            args = ['[' + this.constructor.name + ']'];
            for (var i = 0; i < arguments.length; i++) {
                args.push(arguments[i]);
            }
            console.log(args.join(' '));
        } else if (typeof str === 'string' && str.indexOf('{0}') > -1) {
            args = arguments;
            str = str.replace(/\{([0-9]+)\}/g, function (match, index) {
                return args[parseInt(index) + 1];
            });
            console.log('[' + this.constructor.name + ']', str);
        }
    }
    return this;
};

View.prototype.throw = function(str) {
    var args;
    if ((typeof str === 'string' && str.indexOf('{0}') == -1) || (typeof str !== 'string')) {
        args = ['[' + this.constructor.name + ']'];
        for (var i = 0; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        throw args.join(' ');
    } else if (typeof str === 'string' && str.indexOf('{0}') > -1) {
        args = arguments;
        str = str.replace(/\{([0-9]+)\}/g, function (match, index) {
            return args[parseInt(index) + 1];
        });
        throw ('[' + this.constructor.name + ']' + str);
    }
};

View.prototype.component = function(options) {
    if (typeof options !== 'object') {
        options = {};
    }
    for (var i in options) {
        this.attributes[i] = options[i];
    }
};

View.prototype.patch = function() {
    // Patch targets
    var targets = [ 'controller', 'view' ],
        prototypes = Object.keys(View.prototype);
    for (var i = 0; i < targets.length; i++) {
        var target = targets[i];
        if (typeof this[target] === 'function') {
            for (var j = 0; j < prototypes.length; j++) {
                this[target].prototype[prototypes[j]] = this[prototypes[j]].bind(this);
            }
        }
    }
};

View.prototype.stylesheet = function(list) {
    var args = [];
    if (list instanceof Array) {
        args = list;
    } else {
        args = arguments;
    }
    for (var i = 0; i < args.length; i++) {
        // Get stylesheet path
        var path  = [ '..', 'styles', 'dist', args[i] ].join('/'),
            links = document.head.querySelectorAll('link[href="' + path + '"]');
        // Only link css if stylesheet is not present in the current html
        if (links.length === 0) {
            // Generate a link element
            var link = document.createElement('link');
            link.setAttribute('href', path);
            link.setAttribute('rel', 'stylesheet');
            // Append the stylesheet to html head
            document.head.appendChild(link);
        }
    }
};

View.prototype.autoconfig = function(element, isInit, context) {
    if (!(this instanceof View)) {
        View.prototype.throw.call(this, 'NOT_VIEW_INSTANCE');
    }
    if (typeof this.unload === 'function') {
        context.onunload = this.unload.call(this);
    }
};

View.prototype.unload = function() {
    this.set('initialized', false);
};

App.Common.View = View;

<|endoftext|>