<commit_before>define( [ "Ember" ], function( Ember ) {

	var get = Ember.get,
	    set = Ember.set;

	var CSSMediaRule     = window.CSSMediaRule,
	    CSSStyleRule     = window.CSSStyleRule,
	    reMinWidth       = /^(?:\(max-width:\s*\d+px\)\s*and\s*)?\(min-width:\s*(\d+)px\)$/,
	    cachedMinWidths  = {};

	var cssMinWidthRules = [].filter.call( document.styleSheets[0].rules, function( rule ) {
		return rule instanceof CSSMediaRule
		    && rule.media.length === 1
		    && reMinWidth.test( rule.media[0] )
		    && rule.cssRules.length > 0;
	});
	var cachedMinHeights = [].filter.call( document.styleSheets[0].rules, function( rule ) {
		return rule instanceof CSSStyleRule
		    && rule.style.minHeight !== "";
	}).reduce(function( cache, rule ) {
		cache[ rule.selectorText ] = parseInt( rule.style.minHeight );
		return cache;
	}, {} );

	/**
	 * Generate a list of all min-width media queries and their item widths for a specific selector.
	 * These media queries have been defined by the lesscss function .dynamic-elems-per-row()
	 */
	function readMinWidths( selector ) {
		if ( cachedMinWidths.hasOwnProperty( selector ) ) {
			return cachedMinWidths[ selector ];
		}

		var data = cssMinWidthRules.filter(function( rule ) {
			return rule.cssRules[0].selectorText === selector;
		});

		if ( !data.length ) {
			throw new Error( "Invalid selector" );
		}

		data = data.map( function( rule ) {
			return {
				minWidth: Math.floor( reMinWidth.exec( rule.media[0] )[1] ),
				numItems: Math.floor( 100 / parseInt( rule.cssRules[0].style.width ) )
			};
		});

		return ( cachedMinWidths[ selector ] = data );
	}

	function readMinHeights( selector ) {
		if ( !cachedMinHeights.hasOwnProperty( selector ) ) {
			throw new Error( "Invalid selector" );
		}
		return cachedMinHeights[ selector ];
	}

	function getNeededColumns( selector ) {
		return readMinWidths( selector ).reduce(function( current, next ) {
			return window.innerWidth < next.minWidth
				? current
				: next;
		}).numItems;
	}

	function getNeededRows( selector ) {
		var minHeight = readMinHeights( selector );
		return 1 + Math.ceil( getItemContainer().clientHeight / minHeight );
	}

	function getItemContainer() {
		// the route's view hasn't been inserted yet: choose the parent element
		return document.querySelector( "body > .wrapper" )
		    || document.body;
	}

	return Ember.Mixin.create({
		/**
		 * Define the content array location.
		 * Can't use a binding here!!!
		 */
		contentPath: "controller.model",

		/**
		 * Don't fetch infinitely.
		 */
		maxAutoFetches: 3,

		/**
		 * This is actually an ugly concept, but we need to set this data at the route, so we can
		 * control the fetch size before querying the data. The fetch size depends on the
		 * window size and css media queries containing this selector. We can't move this
		 * calculation to the view or to the controller, because they both aren't set up at the
		 * time where the size is being calculated...
		 */
		itemSelector: "",

		/**
		 * Calculate how many items are needed to completely fill the container
		 */
		calcFetchSize: function() {
			var itemSel = get( this, "itemSelector" );
			var offset  = get( this, "offset" );
			var columns = getNeededColumns( itemSel );
			var rows    = getNeededRows( itemSel );
			var uneven  = offset % columns;
			var limit   = ( columns * rows ) + ( uneven > 0 ? columns - uneven : 0 );

			// fetch size + number of items to fill the last row after a window resize
			set( this, "limit", limit );
		},

		beforeModel: function() {
			this._super.apply( this, arguments );

			// reset on route change
			set( this, "offset", 0 );
			this.calcFetchSize();
		},

		setupController: function( controller, model ) {
			this._super.apply( this, arguments );

			// late bindings
			var binding = get( this, "_binding_offset" );
			if ( !binding ) {
				var contentPath = get( this, "contentPath" );
				binding = Ember.Binding.from( contentPath + ".length" ).to( "offset" );
				set( this, "_binding_offset", binding );
			}
			binding.connect( this );

			var offset = get( this, "offset" );
			var limit  = get( this, "limit" );

			set( controller, "isFetching", false );
			set( controller, "hasFetchedAll", offset < limit );
			set( controller, "initialFetchSize", model.length );
		},

		fetchContent: function() {
			return this.model();
		},

		actions: {
			"willTransition": function () {
				var binding = get( this, "_binding_offset" );
				if ( binding ) {
					binding.disconnect( this );
				}
			},

			"willFetchContent": function( force ) {
				var controller = get( this, "controller" );
				var isFetching = get( controller, "isFetching" );
				var fetchedAll = get( controller, "hasFetchedAll" );

				// we're already busy or finished fetching
				if ( isFetching || fetchedAll ) { return; }

				this.calcFetchSize();

				var content = get( this, get( this, "contentPath" ) );
				var offset  = get( this, "offset" );
				var limit   = get( this, "limit" );
				var max     = get( this, "maxAutoFetches" );
				var num     = offset / limit;

				// don't fetch infinitely
				if ( !force && num > max ) { return; }

				set( controller, "fetchError", false );
				set( controller, "isFetching", true );

				// fetch content
				this.fetchContent()
					.then(function( data ) {
						if ( !data || !data.length || data.length < limit ) {
							set( controller, "hasFetchedAll", true );
						}
						if ( data && data.length ) {
							content.pushObjects( data );
						}
						set( controller, "isFetching", false );
					})
					.catch(function( err ) {
						set( controller, "fetchError", true );
						set( controller, "isFetching", false );
						set( controller, "hasFetchedAll", false );
						return Promise.reject( err );
					});
			}
		}
	});

});
<commit_msg>Limit InfiniteScrollMixin's Twitch API fetch size<commit_after>define( [ "Ember" ], function( Ember ) {

	var get = Ember.get,
	    set = Ember.set;

	var CSSMediaRule     = window.CSSMediaRule,
	    CSSStyleRule     = window.CSSStyleRule,
	    reMinWidth       = /^(?:\(max-width:\s*\d+px\)\s*and\s*)?\(min-width:\s*(\d+)px\)$/,
	    cachedMinWidths  = {};

	var cssMinWidthRules = [].filter.call( document.styleSheets[0].rules, function( rule ) {
		return rule instanceof CSSMediaRule
		    && rule.media.length === 1
		    && reMinWidth.test( rule.media[0] )
		    && rule.cssRules.length > 0;
	});
	var cachedMinHeights = [].filter.call( document.styleSheets[0].rules, function( rule ) {
		return rule instanceof CSSStyleRule
		    && rule.style.minHeight !== "";
	}).reduce(function( cache, rule ) {
		cache[ rule.selectorText ] = parseInt( rule.style.minHeight );
		return cache;
	}, {} );

	/**
	 * Generate a list of all min-width media queries and their item widths for a specific selector.
	 * These media queries have been defined by the lesscss function .dynamic-elems-per-row()
	 */
	function readMinWidths( selector ) {
		if ( cachedMinWidths.hasOwnProperty( selector ) ) {
			return cachedMinWidths[ selector ];
		}

		var data = cssMinWidthRules.filter(function( rule ) {
			return rule.cssRules[0].selectorText === selector;
		});

		if ( !data.length ) {
			throw new Error( "Invalid selector" );
		}

		data = data.map( function( rule ) {
			return {
				minWidth: Math.floor( reMinWidth.exec( rule.media[0] )[1] ),
				numItems: Math.floor( 100 / parseInt( rule.cssRules[0].style.width ) )
			};
		});

		return ( cachedMinWidths[ selector ] = data );
	}

	function readMinHeights( selector ) {
		if ( !cachedMinHeights.hasOwnProperty( selector ) ) {
			throw new Error( "Invalid selector" );
		}
		return cachedMinHeights[ selector ];
	}

	function getNeededColumns( selector ) {
		return readMinWidths( selector ).reduce(function( current, next ) {
			return window.innerWidth < next.minWidth
				? current
				: next;
		}).numItems;
	}

	function getNeededRows( selector ) {
		var minHeight = readMinHeights( selector );
		return 1 + Math.ceil( getItemContainer().clientHeight / minHeight );
	}

	function getItemContainer() {
		// the route's view hasn't been inserted yet: choose the parent element
		return document.querySelector( "body > .wrapper" )
		    || document.body;
	}

	return Ember.Mixin.create({
		/**
		 * Define the content array location.
		 * Can't use a binding here!!!
		 */
		contentPath: "controller.model",

		/**
		 * Don't fetch infinitely.
		 */
		maxAutoFetches: 3,

		/**
		 * Set by Twitch
		 */
		maxLimit: 100,

		/**
		 * This is actually an ugly concept, but we need to set this data at the route, so we can
		 * control the fetch size before querying the data. The fetch size depends on the
		 * window size and css media queries containing this selector. We can't move this
		 * calculation to the view or to the controller, because they both aren't set up at the
		 * time where the size is being calculated...
		 */
		itemSelector: "",

		/**
		 * Calculate how many items are needed to completely fill the container
		 */
		calcFetchSize: function() {
			var itemSel = get( this, "itemSelector" );
			var offset  = get( this, "offset" );
			var max     = get( this, "maxLimit" );
			var columns = getNeededColumns( itemSel );
			var rows    = getNeededRows( itemSel );
			var uneven  = offset % columns;

			// fetch size + number of items to fill the last row after a window resize
			var limit   = ( columns * rows ) + ( uneven > 0 ? columns - uneven : 0 );

			set( this, "limit", Math.min( limit, max ) );
		},

		beforeModel: function() {
			this._super.apply( this, arguments );

			// reset on route change
			set( this, "offset", 0 );
			this.calcFetchSize();
		},

		setupController: function( controller, model ) {
			this._super.apply( this, arguments );

			// late bindings
			var binding = get( this, "_binding_offset" );
			if ( !binding ) {
				var contentPath = get( this, "contentPath" );
				binding = Ember.Binding.from( contentPath + ".length" ).to( "offset" );
				set( this, "_binding_offset", binding );
			}
			binding.connect( this );

			var offset = get( this, "offset" );
			var limit  = get( this, "limit" );

			set( controller, "isFetching", false );
			set( controller, "hasFetchedAll", offset < limit );
			set( controller, "initialFetchSize", model.length );
		},

		fetchContent: function() {
			return this.model();
		},

		actions: {
			"willTransition": function () {
				var binding = get( this, "_binding_offset" );
				if ( binding ) {
					binding.disconnect( this );
				}
			},

			"willFetchContent": function( force ) {
				var controller = get( this, "controller" );
				var isFetching = get( controller, "isFetching" );
				var fetchedAll = get( controller, "hasFetchedAll" );

				// we're already busy or finished fetching
				if ( isFetching || fetchedAll ) { return; }

				this.calcFetchSize();

				var content = get( this, get( this, "contentPath" ) );
				var offset  = get( this, "offset" );
				var limit   = get( this, "limit" );
				var max     = get( this, "maxAutoFetches" );
				var num     = offset / limit;

				// don't fetch infinitely
				if ( !force && num > max ) { return; }

				set( controller, "fetchError", false );
				set( controller, "isFetching", true );

				// fetch content
				this.fetchContent()
					.then(function( data ) {
						if ( !data || !data.length || data.length < limit ) {
							set( controller, "hasFetchedAll", true );
						}
						if ( data && data.length ) {
							content.pushObjects( data );
						}
						set( controller, "isFetching", false );
					})
					.catch(function( err ) {
						set( controller, "fetchError", true );
						set( controller, "isFetching", false );
						set( controller, "hasFetchedAll", false );
						return Promise.reject( err );
					});
			}
		}
	});

});
<|endoftext|>


<commit_before>import path from 'path';
import {
    appendFileSync,
    existsSync,
    lstatSync,
    mkdirSync,
    readdirSync,
    writeFileSync,
} from 'fs';
import webpack from 'webpack';
import del from 'del';
import CleanSelfWebpackPlugin from './clean_self_webpack_plugin';

const sandboxDir = path.resolve(process.cwd(), '__sandbox__');
const entryFile = path.resolve(sandboxDir, 'src/entry.js');
const buildDir = path.resolve(sandboxDir, 'build/');
const srcDir = path.resolve(sandboxDir, 'src/');

function createSrcBundle(numberOfBundles = 1) {
    del.sync(
        [
            path.resolve(srcDir, '**/*'),
            `!${path.resolve(srcDir, '.gitignore')}`,
        ],
        { dot: true },
    );

    /**
     * setup initial dummy entryFile
     */
    writeFileSync(entryFile, `'use strict';\n\n`);

    let count = 1;
    while (count < numberOfBundles) {
        const filename = `${count}.js`;

        appendFileSync(
            entryFile,
            `require.ensure([], require => require('./${filename}'));\n`,
        );

        writeFileSync(
            path.resolve(sandboxDir, `src/${filename}`),
            `'use strict';\n\nmodule.exports = '${filename}';\n`,
        );

        count += 1;
    }
}

/**
 * Clean ALL files in sandbox's src and build directories
 */
function cleanSandbox() {
    del.sync(
        [
            path.resolve(buildDir, '**/*'),
            path.resolve(srcDir, '**/*'),
            `!${path.resolve(srcDir, '.gitignore')}`,
            `!${path.resolve(buildDir, '.gitignore')}`,
        ],
        { dot: true },
    );
}

function createStaticFiles() {
    writeFileSync(
        path.resolve(sandboxDir, 'build/static2.txt'),
        'static2.txt\n',
    );

    writeFileSync(
        path.resolve(sandboxDir, 'build/static1.js'),
        `'use strict';\n\nmodule.exports = 'static1.js';\n`,
    );
}

function resetSandbox() {
    /**
     * Prepare directories
     */
    cleanSandbox();

    /**
     * Add static files that are not part of the webpack bundle
     */
    createStaticFiles();

    /**
     * Create initial entry.js without any additional bundles
     */
    createSrcBundle(1);
}

/**
 * returns a recursive flattened list of files inside a directory
 *
 * Example:
 * [
 *     'js/bundle.js',
 *     'js/chunks/0.bundle.js',
 *     'static1.js',
 *     'static2.txt',
 * ]
 */
function getBuildFiles(rootDir) {
    const getFiles = (dir, basePath = '.') => {
        return readdirSync(dir).reduce((acc, file) => {
            const pathname = path.resolve(rootDir, basePath, file);

            if (lstatSync(pathname).isDirectory()) {
                const nestedDir = getFiles(pathname, path.join(basePath, file));

                return [...acc, ...nestedDir];
            }

            const relativePath = path.join(basePath, file);

            return [...acc, relativePath];
        }, []);
    };

    const files = getFiles(rootDir);

    return files;
}

function createSandbox() {
    if (existsSync(sandboxDir) === false) {
        mkdirSync(sandboxDir);
    }

    if (existsSync(buildDir) === false) {
        mkdirSync(buildDir);
    }

    if (existsSync(srcDir) === false) {
        mkdirSync(srcDir);
    }
}

describe('CleanSelfWebpackPlugin', () => {
    let consoleSpy;

    beforeAll(() => {
        createSandbox();
    });

    beforeEach(() => {
        consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
        resetSandbox();
    });

    afterEach(() => {
        consoleSpy.mockReset();
    });

    afterAll(() => {
        cleanSandbox();
    });

    it('adds files to current assets', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();
        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                '0.bundle.js',
                'bundle.js',
            ]);

            done();
        });
    });

    it('removes only webpack files', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                '0.bundle.js',
                'bundle.js',
            ]);

            createSrcBundle(1);
            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'bundle.js',
                    'static1.js',
                    'static2.txt',
                ]);

                done();
            });
        });
    });

    it('removes nested files', done => {
        createSrcBundle(3);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'js/bundle.js',
                chunkFilename: 'js/chunks/[name].bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                'js/chunks/0.bundle.js',
                'js/chunks/1.bundle.js',
                'js/bundle.js',
            ]);

            createSrcBundle(2);
            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'js/chunks/0.bundle.js',
                    'js/bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'js/bundle.js',
                    'js/chunks/0.bundle.js',
                    'static1.js',
                    'static2.txt',
                ]);

                done();
            });
        });
    });

    it('does nothing when nothing changes or files added but not removed', done => {
        createSrcBundle(1);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();
        const removeFilesSpy = jest.spyOn(
            cleanSelfWebpackPlugin,
            'removeFiles',
        );

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual(['bundle.js']);

            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'bundle.js',
                    'static1.js',
                    'static2.txt',
                ]);

                createSrcBundle(2);
                compiler.run(() => {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        '0.bundle.js',
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        '0.bundle.js',
                        'bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);

                    expect(removeFilesSpy).not.toHaveBeenCalled();

                    done();
                });
            });
        });
    });

    /**
     * customPatterns option
     */
    it('removes with customPatterns', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            customPatterns: ['static*.*', '!static1.js'],
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                '0.bundle.js',
                'bundle.js',
            ]);

            expect(getBuildFiles(buildDir)).toEqual([
                '0.bundle.js',
                'bundle.js',
                'static1.js',
            ]);

            createSrcBundle(1);
            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'bundle.js',
                    'static1.js',
                ]);

                done();
            });
        });
    });

    /**
     * dryRun option
     */
    it('respects dryRun option (force verbose)', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            dryRun: true,
            verbose: false,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                '0.bundle.js',
                'bundle.js',
            ]);

            createSrcBundle(1);
            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                    'static1.js',
                    'static2.txt',
                ]);

                expect(consoleSpy).toHaveBeenCalledWith(
                    'clean-self-webpack-plugin: dryRun 0.bundle.js',
                );

                done();
            });
        });
    });

    /**
     * Verbose option
     */
    it('respects verbose option - true', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            verbose: true,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        compiler.run(() => {
            createSrcBundle(1);
            compiler.run(() => {
                expect(consoleSpy).toHaveBeenCalledWith(
                    'clean-self-webpack-plugin: removed 0.bundle.js',
                );

                done();
            });
        });
    });

    it('respects verbose option - false', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            verbose: false,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        compiler.run(() => {
            createSrcBundle(1);
            compiler.run(() => {
                expect(consoleSpy).not.toHaveBeenCalled();

                done();
            });
        });
    });

    /**
     * initialPatterns option
     */
    it('handles the initialPatterns option (only calls once)', done => {
        createSrcBundle(1);

        const initialBuildFiles = getBuildFiles(buildDir);
        expect(initialBuildFiles).toEqual(['static1.js', 'static2.txt']);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            initialPatterns: ['static2.txt'],
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);

        compiler.run(() => {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual(['bundle.js']);

            expect(getBuildFiles(buildDir)).toEqual([
                'bundle.js',
                'static1.js',
            ]);

            createStaticFiles();

            compiler.run(() => {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'bundle.js',
                    'static1.js',
                    'static2.txt',
                ]);
                done();
            });
        });
    });
});
<commit_msg>handle failing async jest tests<commit_after>import path from 'path';
import {
    appendFileSync,
    existsSync,
    lstatSync,
    mkdirSync,
    readdirSync,
    writeFileSync,
} from 'fs';
import webpack from 'webpack';
import del from 'del';
import CleanSelfWebpackPlugin from './clean_self_webpack_plugin';

const sandboxDir = path.resolve(process.cwd(), '__sandbox__');
const entryFile = path.resolve(sandboxDir, 'src/entry.js');
const buildDir = path.resolve(sandboxDir, 'build/');
const srcDir = path.resolve(sandboxDir, 'src/');

function createSrcBundle(numberOfBundles = 1) {
    del.sync(
        [
            path.resolve(srcDir, '**/*'),
            `!${path.resolve(srcDir, '.gitignore')}`,
        ],
        { dot: true },
    );

    /**
     * setup initial dummy entryFile
     */
    writeFileSync(entryFile, `'use strict';\n\n`);

    let count = 1;
    while (count < numberOfBundles) {
        const filename = `${count}.js`;

        appendFileSync(
            entryFile,
            `require.ensure([], require => require('./${filename}'));\n`,
        );

        writeFileSync(
            path.resolve(sandboxDir, `src/${filename}`),
            `'use strict';\n\nmodule.exports = '${filename}';\n`,
        );

        count += 1;
    }
}

/**
 * Clean ALL files in sandbox's src and build directories
 */
function cleanSandbox() {
    del.sync(
        [
            path.resolve(buildDir, '**/*'),
            path.resolve(srcDir, '**/*'),
            `!${path.resolve(srcDir, '.gitignore')}`,
            `!${path.resolve(buildDir, '.gitignore')}`,
        ],
        { dot: true },
    );
}

function createStaticFiles() {
    writeFileSync(
        path.resolve(sandboxDir, 'build/static2.txt'),
        'static2.txt\n',
    );

    writeFileSync(
        path.resolve(sandboxDir, 'build/static1.js'),
        `'use strict';\n\nmodule.exports = 'static1.js';\n`,
    );
}

function resetSandbox() {
    /**
     * Prepare directories
     */
    cleanSandbox();

    /**
     * Add static files that are not part of the webpack bundle
     */
    createStaticFiles();

    /**
     * Create initial entry.js without any additional bundles
     */
    createSrcBundle(1);
}

/**
 * returns a recursive flattened list of files inside a directory
 *
 * Example:
 * [
 *     'js/bundle.js',
 *     'js/chunks/0.bundle.js',
 *     'static1.js',
 *     'static2.txt',
 * ]
 */
function getBuildFiles(rootDir) {
    const getFiles = (dir, basePath = '.') => {
        return readdirSync(dir).reduce((acc, file) => {
            const pathname = path.resolve(rootDir, basePath, file);

            if (lstatSync(pathname).isDirectory()) {
                const nestedDir = getFiles(pathname, path.join(basePath, file));

                return [...acc, ...nestedDir];
            }

            const relativePath = path.join(basePath, file);

            return [...acc, relativePath];
        }, []);
    };

    const files = getFiles(rootDir);

    return files;
}

function createSandbox() {
    if (existsSync(sandboxDir) === false) {
        mkdirSync(sandboxDir);
    }

    if (existsSync(buildDir) === false) {
        mkdirSync(buildDir);
    }

    if (existsSync(srcDir) === false) {
        mkdirSync(srcDir);
    }
}

describe('CleanSelfWebpackPlugin', () => {
    let consoleSpy;

    beforeAll(() => {
        createSandbox();
    });

    beforeEach(() => {
        consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
        resetSandbox();
    });

    afterEach(() => {
        consoleSpy.mockReset();
    });

    afterAll(() => {
        cleanSandbox();
    });

    it('adds files to current assets', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();
        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                ]);

                done();
            } catch (error) {
                done.fail(error);
            }
        });
    });

    it('removes only webpack files', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            createSrcBundle(1);
            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        'bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    it('removes nested files', done => {
        createSrcBundle(3);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'js/bundle.js',
                chunkFilename: 'js/chunks/[name].bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'js/chunks/0.bundle.js',
                    'js/chunks/1.bundle.js',
                    'js/bundle.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            createSrcBundle(2);
            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'js/chunks/0.bundle.js',
                        'js/bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        'js/bundle.js',
                        'js/chunks/0.bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    it('does nothing when nothing changes or files added but not removed', done => {
        createSrcBundle(1);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin();
        const removeFilesSpy = jest.spyOn(
            cleanSelfWebpackPlugin,
            'removeFiles',
        );

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        'bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);
                } catch (error) {
                    done.fail(error);
                }
                createSrcBundle(2);
                compiler.run(() => {
                    try {
                        expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                            '0.bundle.js',
                            'bundle.js',
                        ]);

                        expect(getBuildFiles(buildDir)).toEqual([
                            '0.bundle.js',
                            'bundle.js',
                            'static1.js',
                            'static2.txt',
                        ]);

                        expect(removeFilesSpy).not.toHaveBeenCalled();

                        done();
                    } catch (error) {
                        done.fail(error);
                    }
                });
            });
        });
    });

    /**
     * customPatterns option
     */
    it('removes with customPatterns', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            customPatterns: ['static*.*', '!static1.js'],
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                    'static1.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            createSrcBundle(1);
            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        'bundle.js',
                        'static1.js',
                    ]);

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    /**
     * dryRun option
     */
    it('respects dryRun option (force verbose)', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            dryRun: true,
            verbose: false,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    '0.bundle.js',
                    'bundle.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            createSrcBundle(1);
            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        '0.bundle.js',
                        'bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);

                    expect(consoleSpy).toHaveBeenCalledWith(
                        'clean-self-webpack-plugin: dryRun 0.bundle.js',
                    );

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    /**
     * Verbose option
     */
    it('respects verbose option - true', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            verbose: true,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        compiler.run(() => {
            createSrcBundle(1);
            compiler.run(() => {
                try {
                    expect(consoleSpy).toHaveBeenCalledWith(
                        'clean-self-webpack-plugin: removed 0.bundle.js',
                    );

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    it('respects verbose option - false', done => {
        createSrcBundle(2);

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            verbose: false,
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        compiler.run(() => {
            createSrcBundle(1);
            compiler.run(() => {
                try {
                    expect(consoleSpy).not.toHaveBeenCalled();

                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });

    /**
     * initialPatterns option
     */
    it('handles the initialPatterns option (only calls once)', done => {
        createSrcBundle(1);

        const initialBuildFiles = getBuildFiles(buildDir);
        try {
            expect(initialBuildFiles).toEqual(['static1.js', 'static2.txt']);
        } catch (error) {
            done.fail(error);
        }

        const cleanSelfWebpackPlugin = new CleanSelfWebpackPlugin({
            initialPatterns: ['static2.txt'],
        });

        const compiler = webpack({
            entry: entryFile,
            output: {
                path: buildDir,
                filename: 'bundle.js',
            },
            plugins: [cleanSelfWebpackPlugin],
        });

        try {
            expect(cleanSelfWebpackPlugin.currentAssets).toEqual([]);
        } catch (error) {
            done.fail(error);
        }

        compiler.run(() => {
            try {
                expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                    'bundle.js',
                ]);

                expect(getBuildFiles(buildDir)).toEqual([
                    'bundle.js',
                    'static1.js',
                ]);
            } catch (error) {
                done.fail(error);
            }

            createStaticFiles();
            compiler.run(() => {
                try {
                    expect(cleanSelfWebpackPlugin.currentAssets).toEqual([
                        'bundle.js',
                    ]);

                    expect(getBuildFiles(buildDir)).toEqual([
                        'bundle.js',
                        'static1.js',
                        'static2.txt',
                    ]);
                    done();
                } catch (error) {
                    done.fail(error);
                }
            });
        });
    });
});
<|endoftext|>


<commit_before>import React from "react";
import {animateScroll} from "react-scroll";
import {Sticky} from "react-sticky";

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

    this.state = {
      isVisible: false
    }
  }

  onStickyChange() {
    this.setState({isVisible: !this.state.isVisible});
  }

  render() {
    const isVisible = this.state.isVisible;

    return (
      <Sticky onStickyStateChange={ this.onStickyChange.bind(this) } topOffset={ 200 }
              className={ isVisible ? 'visible' : 'hidden' }>
        {() => (

 animateScroll.scrollToTop() } className="sticky-header">
            KLICKA HÄR FÖR ATT GÅ TILL TOPPEN

        )}
      </Sticky>
    )
  }
}<commit_msg>Remove text from the 'sticky' header<commit_after>import React from "react";
import {animateScroll} from "react-scroll";
import {Sticky} from "react-sticky";

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

    this.state = {
      isVisible: false
    }
  }

  onStickyChange() {
    this.setState({isVisible: !this.state.isVisible});
  }

  render() {
    const isVisible = this.state.isVisible;

    return (
      <Sticky onStickyStateChange={ this.onStickyChange.bind(this) } topOffset={ 200 }
              className={ isVisible ? 'visible' : 'hidden' }>
        {() => (

 animateScroll.scrollToTop() } className="sticky-header"/>
        )}
      </Sticky>
    )
  }
}<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(API, Utils, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  function resize(rel, w) {
    var flex = w.toString() + 'px';
    rel.style.webkitFlexBasis = flex;
    rel.style.mozFflexBasis = flex;
    rel.style.msFflexBasis = flex;
    rel.style.oFlexBasis = flex;
    rel.style.flexBasis = flex;
  }

  function createEntry(v, head) {
    var label = v.label || '';
    if ( v.label ) {
      delete v.label;
    }

    var checks = {grow: 1, shrink: 1, basis: ''};
    Object.keys(checks).forEach(function(k) {
      if ( typeof v[k] === 'undefined' ) {
        v[k] = checks[k];
      }
    });

    var nel = GUI.Helpers.createElement('gui-list-view-column', v);
    if ( typeof label === 'function' ) {
      nel.appendChild(label.call(nel, nel, v));
    } else {
      nel.appendChild(document.createTextNode(label));
    }

    return nel;
  }

  function createResizers(el) {
    // TODO: These do not work properly :/
    var head = el.querySelector('gui-list-view-head');
    var body = el.querySelector('gui-list-view-body');
    var cols = head.querySelectorAll('gui-list-view-column');

    head.querySelectorAll('gui-list-view-column-resizer').forEach(function(rel) {
      Utils.$remove(rel);
    });

    cols.forEach(function(col, idx) {
      var attr = col.getAttribute('data-resizable');
      if ( attr === 'true' ) {
        var resizer = document.createElement('gui-list-view-column-resizer');
        col.appendChild(resizer);

        var startWidth = 0;
        var maxWidth   = 0;

        GUI.Helpers.createDrag(resizer, function(ev) {
          startWidth = col.offsetWidth;
          maxWidth = el.offsetWidth * 0.85; // FIXME
        }, function(ev, diff) {
          var newWidth = startWidth + diff.x;
          if ( !isNaN(newWidth) ) { //&& newWidth > 0 && newWidth < maxWidth ) {
            resize(col, newWidth);

            // FIXME: Super slow!
            body.querySelectorAll('gui-list-view-row').forEach(function(row) {
              resize(row.children[idx], newWidth);
            });
          }
        });
      }
    });
  }

  function initRow(el, row) {
    var cols = el.querySelectorAll('gui-list-view-head gui-list-view-column');
    var headContainer = el.querySelector('gui-list-view-head');

    row.querySelectorAll('gui-list-view-column').forEach(function(cel, idx) {
      var cl = cols.length;
      var x = cl ? idx % cl : idx;

      GUI.Helpers.setFlexbox(cel, null, null, null, cols[x]);

      var icon = cel.getAttribute('data-icon');
      if ( icon && icon !== 'null' ) {
        Utils.$addClass(cel, 'gui-has-image');
        cel.style.backgroundImage = 'url(' + icon + ')';
      }

      var text = cel.firstChild;
      if ( text && text.nodeType === 3 ) {
        var span = document.createElement('span');
        span.appendChild(document.createTextNode(text.nodeValue));
        cel.insertBefore(span, text);
        cel.removeChild(text);
      }

      if ( el._columns[idx] && !el._columns[idx].visible ) {
        cel.style.display = 'none';
      }
    });

    GUI.Elements._dataview.bindEntryEvents(el, row, 'gui-list-view-row');
  }

  function createRow(e) {
    e = e || {};
    if ( e.columns ) {
      var row = GUI.Helpers.createElement('gui-list-view-row', e, ['columns']);

      e.columns.forEach(function(se) {
        row.appendChild(createEntry(se));
      });

      return row;
    }
    return null;
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * Element: 'gui-list-view'
   *
   * A list view with columns.
   *
   * Format for add():
   *
   * {
   *    value: "something or JSON or whatever",
   *    columns: [
   *      {label: "Value for column 1", icon: "Optional icon"},
   *      {label: "Value for column 2", icon: "Optional icon"}
   *    ]
   * }
   *
   * Format for columns (flexbox parameters are also usable):
   * [
   *    {label: "Column 1"},
   *    {label: "Column 2"}
   *
   * ]
   *
   * Parameters:
   *  zebra     boolean       Enable zebra stripes
   *
   * Setters:
   *  columns(arr)  Sets the columns
   *
   * @api OSjs.GUI.Elements.gui-list-view
   * @see OSjs.GUI.Elements._dataview
   * @class
   */
  GUI.Elements['gui-list-view'] = {
    bind: GUI.Elements._dataview.bind,

    values: function(el) {
      var body = el.querySelector('gui-list-view-body');
      return GUI.Elements._dataview.getSelected(el, body.querySelectorAll('gui-list-view-row'));
    },

    get: function(el, param, value, arg) {
      if ( param === 'entry' ) {
        var body = el.querySelector('gui-list-view-body');
        return GUI.Elements._dataview.getEntry(el, body.querySelectorAll('gui-list-view-row'), value, arg);
      }
      return GUI.Helpers.getProperty(el, param);
    },

    set: function(el, param, value, arg) {
      if ( param === 'columns' ) {
        var head = el.querySelector('gui-list-view-head');
        var row = document.createElement('gui-list-view-row');
        Utils.$empty(head);

        el._columns = [];

        value.forEach(function(v) {
          v.visible = (typeof v.visible === 'undefined') || v.visible === true;

          var nel = createEntry(v, true);

          el._columns.push(v);

          if ( !v.visible ) {
            nel.style.display = 'none';
          }
          row.appendChild(nel);

          GUI.Helpers.setFlexbox(nel);
        });

        head.appendChild(row);

        createResizers(el);
        return true;
      } else if ( param === 'selected' || param === 'value' ) {
        var body = el.querySelector('gui-list-view-body');
        GUI.Elements._dataview.setSelected(el, body, body.querySelectorAll('gui-list-view-row'), value, arg);
        return true;
      }

      return false;
    },

    call: function(el, method, args) {
      var body = el.querySelector('gui-list-view-body');
      if ( method === 'add' ) {
        GUI.Elements._dataview.add(el, args, function(e) {
          var row = createRow(e);
          if ( row ) {
            body.appendChild(row);
            initRow(el, row);
          }
        });
      } else if ( method === 'remove' ) {
        GUI.Elements._dataview.remove(el, args, 'gui-list-view-row');
      } else if ( method === 'clear' ) {
        GUI.Elements._dataview.clear(el, el.querySelector('gui-list-view-body'));
      } else if ( method === 'patch' ) {
        GUI.Elements._dataview.patch(el, args, 'gui-list-view-row', body, createRow, initRow);
      } else if ( method === 'focus' ) {
        GUI.Elements._dataview.focus(el);
      }
      return this;
    },

    build: function(el, applyArgs) {
      el._columns  = [];

      var head = el.querySelector('gui-list-view-head');
      var body = el.querySelector('gui-list-view-body');

      // Make sure base elements are in the dom
      if ( !body ) {
        body = document.createElement('gui-list-view-body');
        el.appendChild(body);
      }

      if ( !head ) {
        head = document.createElement('gui-list-view-head');
        el.insertBefore(head, body);
      }

      // Misc UI
      createResizers(el);

      Utils.$bind(el, 'scroll', function(ev) {
        head.style.top = el.scrollTop + 'px';
      }, false);

      // Create scheme defined header
      el.querySelectorAll('gui-list-view-head gui-list-view-column').forEach(function(cel, idx) {
        GUI.Helpers.setFlexbox(cel);

        var vis = cel.getAttribute('data-visible');
        var iter = {
          visible: vis === null || vis === 'true',
          grow: cel.getAttribute('data-grow'),
          shrink: cel.getAttribute('data-shrink'),
          basis: cel.getAttribute('data-basis')
        };

        el._columns.push(iter);

        if ( !iter.visible ) {
          cel.style.display = 'none';
        }
      });

      // Create scheme defined rows
      el.querySelectorAll('gui-list-view-body gui-list-view-row').forEach(function(row) {
        initRow(el, row);
      });

      GUI.Elements._dataview.build(el, applyArgs);
    }
  };

})(OSjs.API, OSjs.Utils, OSjs.VFS, OSjs.GUI);
<commit_msg>GUI: Added onCreated() callback for add()<commit_after>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2015, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(API, Utils, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  function resize(rel, w) {
    var flex = w.toString() + 'px';
    rel.style.webkitFlexBasis = flex;
    rel.style.mozFflexBasis = flex;
    rel.style.msFflexBasis = flex;
    rel.style.oFlexBasis = flex;
    rel.style.flexBasis = flex;
  }

  function createEntry(v, head) {
    var label = v.label || '';

    if ( v.label ) {
      delete v.label;
    }

    var checks = {grow: 1, shrink: 1, basis: ''};
    Object.keys(checks).forEach(function(k) {
      if ( typeof v[k] === 'undefined' ) {
        v[k] = checks[k];
      }
    });

    var nel = GUI.Helpers.createElement('gui-list-view-column', v);
    if ( typeof label === 'function' ) {
      nel.appendChild(label.call(nel, nel, v));
    } else {
      nel.appendChild(document.createTextNode(label));
    }

    return nel;
  }

  function createResizers(el) {
    // TODO: These do not work properly :/
    var head = el.querySelector('gui-list-view-head');
    var body = el.querySelector('gui-list-view-body');
    var cols = head.querySelectorAll('gui-list-view-column');

    head.querySelectorAll('gui-list-view-column-resizer').forEach(function(rel) {
      Utils.$remove(rel);
    });

    cols.forEach(function(col, idx) {
      var attr = col.getAttribute('data-resizable');
      if ( attr === 'true' ) {
        var resizer = document.createElement('gui-list-view-column-resizer');
        col.appendChild(resizer);

        var startWidth = 0;
        var maxWidth   = 0;

        GUI.Helpers.createDrag(resizer, function(ev) {
          startWidth = col.offsetWidth;
          maxWidth = el.offsetWidth * 0.85; // FIXME
        }, function(ev, diff) {
          var newWidth = startWidth + diff.x;
          if ( !isNaN(newWidth) ) { //&& newWidth > 0 && newWidth < maxWidth ) {
            resize(col, newWidth);

            // FIXME: Super slow!
            body.querySelectorAll('gui-list-view-row').forEach(function(row) {
              resize(row.children[idx], newWidth);
            });
          }
        });
      }
    });
  }

  function initRow(el, row) {
    var cols = el.querySelectorAll('gui-list-view-head gui-list-view-column');
    var headContainer = el.querySelector('gui-list-view-head');

    row.querySelectorAll('gui-list-view-column').forEach(function(cel, idx) {
      var cl = cols.length;
      var x = cl ? idx % cl : idx;

      GUI.Helpers.setFlexbox(cel, null, null, null, cols[x]);

      var icon = cel.getAttribute('data-icon');
      if ( icon && icon !== 'null' ) {
        Utils.$addClass(cel, 'gui-has-image');
        cel.style.backgroundImage = 'url(' + icon + ')';
      }

      var text = cel.firstChild;
      if ( text && text.nodeType === 3 ) {
        var span = document.createElement('span');
        span.appendChild(document.createTextNode(text.nodeValue));
        cel.insertBefore(span, text);
        cel.removeChild(text);
      }

      if ( el._columns[idx] && !el._columns[idx].visible ) {
        cel.style.display = 'none';
      }
    });

    GUI.Elements._dataview.bindEntryEvents(el, row, 'gui-list-view-row');
  }

  function createRow(e) {
    e = e || {};
    if ( e.columns ) {
      var row = GUI.Helpers.createElement('gui-list-view-row', e, ['columns']);

      e.columns.forEach(function(se) {
        row.appendChild(createEntry(se));

      });

      return row;
    }
    return null;
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * Element: 'gui-list-view'
   *
   * A list view with columns.
   *
   * Format for add():
   *
   * {
   *    value: "something or JSON or whatever",
   *    columns: [
   *      {label: "Value for column 1", icon: "Optional icon"},
   *      {label: "Value for column 2", icon: "Optional icon"}
   *    ]
   * }
   *
   * Format for columns (flexbox parameters are also usable):
   * [
   *    {label: "Column 1"},
   *    {label: "Column 2"}
   *
   * ]
   *
   * Parameters:
   *  zebra     boolean       Enable zebra stripes
   *
   * Setters:
   *  columns(arr)  Sets the columns
   *
   * @api OSjs.GUI.Elements.gui-list-view
   * @see OSjs.GUI.Elements._dataview
   * @class
   */
  GUI.Elements['gui-list-view'] = {
    bind: GUI.Elements._dataview.bind,

    values: function(el) {
      var body = el.querySelector('gui-list-view-body');
      return GUI.Elements._dataview.getSelected(el, body.querySelectorAll('gui-list-view-row'));
    },

    get: function(el, param, value, arg) {
      if ( param === 'entry' ) {
        var body = el.querySelector('gui-list-view-body');
        return GUI.Elements._dataview.getEntry(el, body.querySelectorAll('gui-list-view-row'), value, arg);
      }
      return GUI.Helpers.getProperty(el, param);
    },

    set: function(el, param, value, arg) {
      if ( param === 'columns' ) {
        var head = el.querySelector('gui-list-view-head');
        var row = document.createElement('gui-list-view-row');
        Utils.$empty(head);

        el._columns = [];

        value.forEach(function(v) {
          v.visible = (typeof v.visible === 'undefined') || v.visible === true;

          var nel = createEntry(v, true);

          el._columns.push(v);

          if ( !v.visible ) {
            nel.style.display = 'none';
          }
          row.appendChild(nel);

          GUI.Helpers.setFlexbox(nel);
        });

        head.appendChild(row);

        createResizers(el);
        return true;
      } else if ( param === 'selected' || param === 'value' ) {
        var body = el.querySelector('gui-list-view-body');
        GUI.Elements._dataview.setSelected(el, body, body.querySelectorAll('gui-list-view-row'), value, arg);
        return true;
      }

      return false;
    },

    call: function(el, method, args) {
      var body = el.querySelector('gui-list-view-body');
      if ( method === 'add' ) {
        GUI.Elements._dataview.add(el, args, function(e) {
          var cbCreated = e.onCreated || function() {};
          var row = createRow(e);
          if ( row ) {
            body.appendChild(row);
            initRow(el, row);
          }

          cbCreated(row);
        });
      } else if ( method === 'remove' ) {
        GUI.Elements._dataview.remove(el, args, 'gui-list-view-row');
      } else if ( method === 'clear' ) {
        GUI.Elements._dataview.clear(el, el.querySelector('gui-list-view-body'));
      } else if ( method === 'patch' ) {
        GUI.Elements._dataview.patch(el, args, 'gui-list-view-row', body, createRow, initRow);
      } else if ( method === 'focus' ) {
        GUI.Elements._dataview.focus(el);
      }
      return this;
    },

    build: function(el, applyArgs) {
      el._columns  = [];

      var head = el.querySelector('gui-list-view-head');
      var body = el.querySelector('gui-list-view-body');

      // Make sure base elements are in the dom
      if ( !body ) {
        body = document.createElement('gui-list-view-body');
        el.appendChild(body);
      }

      if ( !head ) {
        head = document.createElement('gui-list-view-head');
        el.insertBefore(head, body);
      }

      // Misc UI
      createResizers(el);

      Utils.$bind(el, 'scroll', function(ev) {
        head.style.top = el.scrollTop + 'px';
      }, false);

      // Create scheme defined header
      el.querySelectorAll('gui-list-view-head gui-list-view-column').forEach(function(cel, idx) {
        GUI.Helpers.setFlexbox(cel);

        var vis = cel.getAttribute('data-visible');
        var iter = {
          visible: vis === null || vis === 'true',
          grow: cel.getAttribute('data-grow'),
          shrink: cel.getAttribute('data-shrink'),
          basis: cel.getAttribute('data-basis')
        };

        el._columns.push(iter);

        if ( !iter.visible ) {
          cel.style.display = 'none';
        }
      });

      // Create scheme defined rows
      el.querySelectorAll('gui-list-view-body gui-list-view-row').forEach(function(row) {
        initRow(el, row);
      });

      GUI.Elements._dataview.build(el, applyArgs);
    }
  };

})(OSjs.API, OSjs.Utils, OSjs.VFS, OSjs.GUI);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Phoenix Scholars Co. (http://dpq.co.ir)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
'use strict';

angular.module('pluf')

/**
 * @memberof pluf
 * @ngdoc service
 * @name $monitor
 * @description مدیریت تمام مانیتورها را ایجاد می‌کند
 *
 * مانیتورها در واحد زمان باید به روز شوند. این سرویس نه تنها مانیتورها را ایجاد
 * بلکه آنها را در واحد زمان به روز می‌کند.
 *
 * در صورتی که استفاده از یک مانیتور تمام شود،این سرویس مانیتورهای ایجاد شده را
 * حذف می‌کند تا میزان انتقال اطلاعات را کم کند.
 *
 * @see PMonitor
 *
 */
.service('$collection', function($pluf, PCollection, PObjectFactory) {

	var _cache = new PObjectFactory(function(data) {
		return new PCollection(data);
	});

	/**
	 * Creates new collection
	 *
	 * @memberof $collection
	 * @return Promise<PCollection>
	 * createdreceipt
	 *
	 */
	this.newCollection = $pluf.createNew({
		method : 'POST',
		url : '/api/collection/new'
	}, _cache);

	/**
	 * Gets collection detail
	 *
	 * @memberof $collection
	 * @return Promise<PCollection>
	 * createdreceipt
	 *
	 */
	this.collection = $pluf.createGet({
		method : 'GET',
		url : '/api/collection/{id}',
	}, _cache);

	/**
	 * Lists all collections
	 *
	 * @memberof $collection
	 * @return Promise<PaginatorPage<PCollection>>
	 * createdreceipt
	 *
	 */
	this.collections = $pluf.createFind({
		method : 'GET',
		url : '/api/collection/find',
	}, _cache);

});
<commit_msg>simple change<commit_after>/*
 * Copyright (c) 2015 Phoenix Scholars Co. (http://dpq.co.ir)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
'use strict';

angular.module('pluf')

/**
 * @memberof pluf
 * @ngdoc service
 * @name $monitor
 * @description مدیریت تمام مانیتورها را ایجاد می‌کند
 *
 * مانیتورها در واحد زمان باید به روز شوند. این سرویس نه تنها مانیتورها را ایجاد
 * بلکه آنها را در واحد زمان به روز می‌کند.
 *
 * در صورتی که استفاده از یک مانیتور تمام شود،این سرویس مانیتورهای ایجاد شده را
 * حذف می‌کند تا میزان انتقال اطلاعات را کم کند.
 *
 * @see PMonitor
 *
 */
.service('$collection', function($pluf, PCollection, PObjectFactory) {

	var _cache = new PObjectFactory(function(data) {
		return new PCollection(data);
	});

	/**
	 * Creates new collection
	 *
	 * @memberof $collection
	 * @return Promise<PCollection>
	 * createdreceipt
	 *
	 */
	this.newCollection = $pluf.createNew({
		method : 'POST',
		url : '/api/collection/new'
	}, _cache);

	/**
	 * Gets collection detail. Input could be id or name of collection.
	 *
	 * @memberof $collection
	 * @return Promise<PCollection>
	 * createdreceipt
	 *
	 */
	this.collection = $pluf.createGet({
		method : 'GET',
		url : '/api/collection/{id}',
	}, _cache);

	/**
	 * Lists all collections
	 *
	 * @memberof $collection
	 * @return Promise<PaginatorPage<PCollection>>
	 * createdreceipt
	 *
	 */
	this.collections = $pluf.createFind({
		method : 'GET',
		url : '/api/collection/find',
	}, _cache);

});
<|endoftext|>


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

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Property',
  ids: [ 'name' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'value',  0 ]
  ],
  actions: [
    {
      name: 'deleteRow',
      label: 'X',
      code: function deleteRow() {
        debugger;
      }
    }
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Clock',
  extends: 'com.google.foam.demos.clock.Clock',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    [ 'mass',  0 ],
    [ 'radius', 45 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Text',
  extends: 'foam.graphics.Label',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'width',  100 ],
    [ 'height', 50 ],
    [ 'text', 'Text' ],
    [ 'color', '#000000' ],
    [ 'font', '50px Arial' ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Circle',
  extends: 'foam.graphics.Circle',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'radius', 25 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Box',
  extends: 'foam.graphics.Box',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'width',  50 ],
    [ 'height', 50 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'LivePaint',
  extends: 'foam.u2.Element',

  implements: [ 'foam.memento.MementoMgr' ],

  requires: [
    'com.google.livepaint.Property',
    'foam.dao.EasyDAO',
    'foam.graphics.Box',
    'foam.graphics.CView',
    'foam.graphics.Circle',
    'foam.physics.Physical',
    'foam.physics.PhysicsEngine',
    'foam.u2.PopupView',
    'foam.u2.TableView'
  ],

  exports: [ 'as data' ],

  constants: {
    SELECTED_COLOR:   '#dddddd',
    UNSELECTED_COLOR: '#ffffff'
  },

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
      ^ { display: flex; }
      ^ > * { padding: 16px; }
      .foam-u2-TableView-selected { background: lightgray; }
      ^ canvas { border: 1px solid black; }
      ^ .foam-u2-ActionView { margin: 10px; }
      ^properties .foam-u2-ActionView { background: white; padding: 0; margin: 2px; border: none; }
      */}
    })
  ],

  properties: [
    {
      name: 'physics',
      factory: function() {
        return this.PhysicsEngine.create();
      }
    },
    'feedback_',
    'currentTool',
    {
      class: 'foam.dao.DAOProperty',
      name: 'tools',
      view: {
        class: 'foam.u2.TableView',
        columns: [ foam.core.Model.NAME ]
      },
      factory: function() {
        var dao = foam.dao.EasyDAO.create({
          of: 'foam.core.Model',
          daoType: 'MDAO'
        });
        dao.put(com.google.foam.demos.robot.Robot.model_);
        dao.put(com.google.livepaint.Clock.model_);
        dao.put(com.google.livepaint.Circle.model_);
        dao.put(com.google.livepaint.Box.model_);
        dao.put(com.google.livepaint.Text.model_);
        return dao;
      }
    },
    {
      name: 'selected',
      view: { class: 'foam.u2.DetailView' },
      postSet: function(o, n) {
        if ( o ) { o.shadowBlur = 0; }
        if ( n ) { n.shadowBlur = 10; n.shadowColor = '#ff0000'; }
      }
    },
    {
      name: 'properties',
      view: {
        class: 'foam.u2.TableView',
        columns: [
          com.google.livepaint.Property.NAME,
          com.google.livepaint.Property.DELETE_ROW
        ]
      },
      factory: function() {
        var dao = foam.dao.EasyDAO.create({
          of: 'com.google.livepaint.Property',
          guid: true,
          seqProperty: this.Property.NAME,
//          daoType: 'MDAO'
          daoType: 'ARRAY'
        });
        dao.put(this.Property.create({name: 'physics', value: this.physics}));
        return dao;
      }
    },
    {
      name: 'canvas',
      factory: function() {
        return this.Box.create({autoRepaint: true, width: 600, height: 500, color: '#f3f3f3'});
      }
    },
  ],

  methods: [

    function initE() {
      this.properties.on.put.sub(this.onPropertyPut);

      this.memento$.sub(function() {
        var m = this.memento;
        if ( this.feedback_ ) return;
        this.canvas.children = [];
        if ( m ) {
          for ( var i = 0 ; i < m.length ; i++ ) {
            var c = m[i];
            this.addCircle(c.x, c.y, c.radius);
          }
        }
        this.selected = null;
      }.bind(this));

      this.
          cssClass(this.myCls()).
          start(this.TOOLS, {selection$: this.currentTool$}).end().
          start('center').
            start(this.BACK,  {label: 'Undo'}).end().
            start(this.FORTH, {label: 'Redo'}).end().
            tag('br').
            start(this.canvas).
              on('click',       this.onClick).
              on('contextmenu', this.onRightClick).
            end().
          end().
          start('div').
            cssClass(this.myCls('properties')).
            add(this.PROPERTIES).
          end().
          add(this.SELECTED);

      this.physics.start();
    },

    function addProperty(value, opt_name, opt_i) {
      var self = this;
      if ( ! opt_name ) {
        var i = opt_i || 1;
        var prefix = value.cls_.name.toLowerCase();
        this.properties.find(prefix + i).then(function (o) {
          if ( ! o ) {
            self.addProperty(value, prefix + i);
          } else {
            self.addProperty(value, null, i+1);
          }
        }).catch(function(x) {
          self.addProperty(value, prefix+i);
        });
      } else {
        this.properties.put(this.Property.create({
          name: opt_name,
          value: value
        }));
      }
    },

    /*
    function addCircle(x, y, opt_r) {
      var c = this.Circle.create({
        x: x,
        y: y,
        radius: opt_r || 25,
        color: this.UNSELECTED_COLOR,
        border: '#000'});

      this.canvas.addChildren(c);

      return c;
    },
    */

    function updateMemento() {
      this.feedback_ = true;
      this.memento = this.canvas.children.map(function(c) {
        return {x: c.x, y: c.y, radius: c.radius};
      });
      this.feedback_ = false;
    }
  ],

  actions: [
    {
      name: 'deleteProperty',
      label: 'X',
      code: function() {
        debugger;
      }
    }
  ],

  listeners: [
    function onPropertyPut(_, _, _, p) {
      var o = p.value;
      if ( this.CView.isInstance(o) ) {
        this.canvas.add(o);

        if ( this.Physical.isInstance(o) ) {
          this.physics.add(o);
        }
      }
    },

    function onClick(evt) {
      var x = evt.offsetX, y = evt.offsetY;
      var c = this.canvas.findFirstChildAt(x, y);

      if ( c ) {
        this.selected = c;
      } else {
        var tool = this.currentTool;
        if ( ! tool ) return;
        var cls = this.lookup(tool.id);
        var o = cls.create({x: x, y: y});
        this.addProperty(o);
        this.selected = o;
        this.updateMemento();
      }
    },

    function onRightClick(evt) {
      evt.preventDefault();

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

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Property',
  ids: [ 'name' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'value',  0 ]
  ],
  actions: [
    {
      name: 'deleteRow',
      label: 'X',
      code: function deleteRow(X) {
        X.properties.remove(X.data);
      }
    }
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Clock',
  extends: 'com.google.foam.demos.clock.Clock',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    [ 'mass',  0 ],
    [ 'radius', 45 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Text',
  extends: 'foam.graphics.Label',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'width',  100 ],
    [ 'height', 50 ],
    [ 'text', 'Text' ],
    [ 'color', '#000000' ],
    [ 'font', '50px Arial' ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Circle',
  extends: 'foam.graphics.Circle',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'radius', 25 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'Box',
  extends: 'foam.graphics.Box',
  implements: [ 'foam.physics.Physical' ],
  properties: [
    {
      class: 'String',
      name: 'name'
    },
    [ 'mass',  0 ],
    [ 'width',  50 ],
    [ 'height', 50 ]
  ]
});

foam.CLASS({
  package: 'com.google.livepaint',
  name: 'LivePaint',
  extends: 'foam.u2.Element',

  implements: [ 'foam.memento.MementoMgr' ],

  requires: [
    'com.google.livepaint.Property',
    'foam.dao.EasyDAO',
    'foam.graphics.Box',
    'foam.graphics.CView',
    'foam.graphics.Circle',
    'foam.physics.Physical',
    'foam.physics.PhysicsEngine',
    'foam.u2.PopupView',
    'foam.u2.TableView'
  ],

  exports: [
    'as data',
    'properties'
  ],

  constants: {
    SELECTED_COLOR:   '#dddddd',
    UNSELECTED_COLOR: '#ffffff'
  },

  axioms: [
    foam.u2.CSS.create({
      code: function() {/*
      ^ { display: flex; }
      ^ > * { padding: 16px; }
      .foam-u2-TableView-selected { background: lightgray; }
      ^ canvas { border: 1px solid black; }
      ^ .foam-u2-ActionView { margin: 10px; }
      ^properties .foam-u2-ActionView { background: white; padding: 0; margin: 2px; border: none; }
      */}
    })
  ],

  properties: [
    {
      name: 'physics',
      factory: function() {
        return this.PhysicsEngine.create();
      }
    },
    'feedback_',
    'currentTool',
    {
      class: 'foam.dao.DAOProperty',
      name: 'tools',
      view: {
        class: 'foam.u2.TableView',
        columns: [ foam.core.Model.NAME ]
      },
      factory: function() {
        var dao = foam.dao.EasyDAO.create({
          of: 'foam.core.Model',
          daoType: 'MDAO'
        });
        dao.put(com.google.foam.demos.robot.Robot.model_);
        dao.put(com.google.livepaint.Clock.model_);
        dao.put(com.google.livepaint.Circle.model_);
        dao.put(com.google.livepaint.Box.model_);
        dao.put(com.google.livepaint.Text.model_);
        return dao;
      }
    },
    {
      name: 'selected',
      view: { class: 'foam.u2.DetailView' },
      postSet: function(o, n) {
        if ( o ) { o.shadowBlur = 0; }
        if ( n ) { n.shadowBlur = 10; n.shadowColor = '#ff0000'; }
      }
    },
    {
      name: 'properties',
      view: {
        class: 'foam.u2.TableView',
        columns: [
          com.google.livepaint.Property.NAME,
          com.google.livepaint.Property.DELETE_ROW
        ]
      },
      factory: function() {
        var dao = foam.dao.EasyDAO.create({
          of: 'com.google.livepaint.Property',
          guid: true,
          seqProperty: this.Property.NAME,
//          daoType: 'MDAO'
          daoType: 'ARRAY'
        });
        dao.put(this.Property.create({name: 'physics', value: this.physics}));
        return dao;
      }
    },
    {
      name: 'canvas',
      factory: function() {
        return this.Box.create({autoRepaint: true, width: 600, height: 500, color: '#f3f3f3'});
      }
    },
  ],

  methods: [

    function initE() {
      this.properties.on.put.sub(this.onPropertyPut);
      this.properties.on.remove.sub(this.onPropertyRemove);

      this.memento$.sub(function() {
        var m = this.memento;
        if ( this.feedback_ ) return;
        this.canvas.children = [];
        if ( m ) {
          for ( var i = 0 ; i < m.length ; i++ ) {
            var c = m[i];
            this.addCircle(c.x, c.y, c.radius);
          }
        }
        this.selected = null;
      }.bind(this));

      this.
          cssClass(this.myCls()).
          start(this.TOOLS, {selection$: this.currentTool$}).end().
          start('center').
            start(this.BACK,  {label: 'Undo'}).end().
            start(this.FORTH, {label: 'Redo'}).end().
            tag('br').
            start(this.canvas).
              on('click',       this.onClick).
              on('contextmenu', this.onRightClick).
            end().
          end().
          start('div').
            cssClass(this.myCls('properties')).
            add(this.PROPERTIES).
          end().
          add(this.SELECTED);

      this.physics.start();
    },

    function addProperty(value, opt_name, opt_i) {
      var self = this;
      if ( ! opt_name ) {
        var i = opt_i || 1;
        var prefix = value.cls_.name.toLowerCase();
        this.properties.find(prefix + i).then(function (o) {
          if ( ! o ) {
            self.addProperty(value, prefix + i);
          } else {
            self.addProperty(value, null, i+1);
          }
        }).catch(function(x) {
          self.addProperty(value, prefix+i);
        });
      } else {
        this.properties.put(this.Property.create({
          name: opt_name,
          value: value
        }));
      }
    },

    /*
    function addCircle(x, y, opt_r) {
      var c = this.Circle.create({
        x: x,
        y: y,
        radius: opt_r || 25,
        color: this.UNSELECTED_COLOR,
        border: '#000'});

      this.canvas.addChildren(c);

      return c;
    },
    */

    function updateMemento() {
      this.feedback_ = true;
      this.memento = this.canvas.children.map(function(c) {
        return {x: c.x, y: c.y, radius: c.radius};
      });
      this.feedback_ = false;
    }
  ],

  actions: [
    {
      name: 'deleteProperty',
      label: 'X',
      code: function(X) {
        debugger;
      }
    }
  ],

  listeners: [
    function onPropertyPut(_, _, _, p) {
      var o = p.value;
      if ( this.CView.isInstance(o) ) {
        this.canvas.add(o);

        if ( this.Physical.isInstance(o) ) {
          this.physics.add(o);
        }
      }
    },

    function onPropertyRemove(_, _, _, p) {
      var o = p.value;
      if ( this.CView.isInstance(o) ) {
        this.canvas.remove(o);

        if ( this.Physical.isInstance(o) ) {
          this.physics.remove(o);
        }
      }
    },

    function onClick(evt) {
      var x = evt.offsetX, y = evt.offsetY;
      var c = this.canvas.findFirstChildAt(x, y);

      if ( c ) {
        this.selected = c;
      } else {
        var tool = this.currentTool;
        if ( ! tool ) return;
        var cls = this.lookup(tool.id);
        var o = cls.create({x: x, y: y});
        this.addProperty(o);
        this.selected = o;
        this.updateMemento();
      }
    },

    function onRightClick(evt) {
      evt.preventDefault();

      if ( ! this.selected ) return;
    }
  ]
});
<|endoftext|>


<commit_before>/** @jsx h */
import { h } from 'preact';
import './style.scss';
import { sleep } from '../../utils/async';
import feature from '../../utils/feature';
import viewer from '../../viewer';
import audio from '../../audio';
import hud from '../../hud';
import enterIconSvg from './icons/entervr.svg';
import enterIconDisabledSvg from './icons/x_entervr.svg';
import ButtonItem from '../ButtonItem';

const toggleVR = async () => {
  if (!feature.hasVR) {
    return;
  }
  if (viewer.vrEffect.isPresenting) {
    viewer.vrEffect.exitPresent();
    viewer.switchCamera('orthographic');
  } else {
    viewer.vrEffect.requestPresent();
    this.removeMessage = hud.enterVR();
    await audio.fadeOut();
    viewer.switchCamera('default');
    await sleep(1000);
    audio.pause();
    audio.rewind();
    await sleep(4000);
    this.removeMessage();
    audio.play();
  }
};

export default ({ label }) => (
  <ButtonItem
    label={label && (feature.hasVR ? 'Enter VR' : 'VR not found')}
    onClick={toggleVR}
    icon={feature.hasVR ? enterIconSvg : enterIconDisabledSvg}
  />
);
<commit_msg>fix entering of VR<commit_after>/** @jsx h */
import { h } from 'preact';
import './style.scss';
import { sleep } from '../../utils/async';
import feature from '../../utils/feature';
import viewer from '../../viewer';
import audio from '../../audio';
import hud from '../../hud';
import enterIconSvg from './icons/entervr.svg';
import enterIconDisabledSvg from './icons/x_entervr.svg';
import ButtonItem from '../ButtonItem';

const toggleVR = async () => {
  if (!feature.hasVR) {
    return;
  }
  if (viewer.vrEffect.isPresenting) {
    viewer.vrEffect.exitPresent();
    viewer.switchCamera('orthographic');
  } else {
    viewer.vrEffect.requestPresent();
    const removeMessage = hud.enterVR();
    await audio.fadeOut();
    viewer.switchCamera('default');
    await sleep(1000);
    audio.pause();
    audio.rewind();
    await sleep(4000);
    removeMessage();
    audio.play();
  }
};

export default ({ label }) => (
  <ButtonItem
    label={label && (feature.hasVR ? 'Enter VR' : 'VR not found')}
    onClick={toggleVR}
    icon={feature.hasVR ? enterIconSvg : enterIconDisabledSvg}
  />
);
<|endoftext|>


<commit_before>import List     from '../../components/List/List.js';
import Note     from '../../models/Note.js';
import NoteView from '../Note/Note.js';
import View     from '../../core/View.js';

export default class NotesList extends View {

  template = document.getElementById(`notes-list-template`);

  constructor(
    notes = [],
    {
      border       = true,
      headingLevel = `h3`,
    } = {},
  ) {
    super();
    this.border       = border;
    this.headingLevel = headingLevel;
    this.notes        = notes;
  }

  addEventListeners() {
    this.el.querySelector(`.js-add-note-button`).addEventListener(`click`, this.addNote.bind(this));
  }

  addNote() {
    this.notes.unshift(new Note);
    this.renderList();
  }

  itemTemplate(data, i) {
    const li   = document.createElement(`li`);
    const view = new NoteView(data, i);
    const el   = view.render();
    li.classList.add(`note-item`);
    li.appendChild(el);
    return li;
  }

  render() {

    this.el      = this.cloneTemplate();
    this.el.view = this;

    if (this.border) this.el.classList.add(`bordered`);

    if (this.headingLevel !== `h3`) {
      const oldHeading = this.el.querySelector(`.js-notes-list-heading`);
      const newHeading = View.fromHTML(`
        <${ this.headingLevel } class='notes-list-heading js-notes-list-heading'>
          Notes
        </${ this.headingLevel }>
      `);
      oldHeading.replaceWith(newHeading);
    }

    this.renderList();
    this.addEventListeners();

    return this.el;

  }

  renderList() {

    const listOptions = {
      classes:  [`notes`],
      name:     `note`,
      template: this.itemTemplate,
    };

    const listView = new List(this.notes, listOptions);
    const newList = listView.render();
    const oldList = this.el.querySelector(`.notes`);
    oldList.view?.events.stop();
    oldList.replaceWith(newList);

  }

}
<commit_msg>emit update events<commit_after>import List     from '../../components/List/List.js';
import Note     from '../../models/Note.js';
import NoteView from '../Note/Note.js';
import View     from '../../core/View.js';

export default class NotesList extends View {

  template = document.getElementById(`notes-list-template`);

  constructor(
    notes = [],
    {
      border       = true,
      headingLevel = `h3`,
    } = {},
  ) {
    super();
    this.border       = border;
    this.headingLevel = headingLevel;
    this.notes        = notes;
  }

  addEventListeners() {
    this.el.addEventListener(`click`, ({ target }) => {

      if (target.classList.contains(`js-add-note-button`)) {
        this.addNote();
      }

      if (target.classList.contains(`js-delete-button`)) {
        const id = target.closest(`li`)?.dataset?.id;
        if (!id) return; // ID is still a string here
        this.deleteNote(Number(id));
      }

      if (target.classList.contains(`js-save-button`)) {
        this.save();
      }

    });
  }

  addNote() {
    this.notes.unshift(new Note);
    this.renderList();
    this.save();
  }

  deleteNote(i) {
    this.notes.splice(i, 1);
    this.renderList();
    this.save();
  }

  save() {
    this.events.emit(`update`);
  }

  itemTemplate(data, i) {
    const li   = document.createElement(`li`);
    const view = new NoteView(data, i);
    const el   = view.render();
    li.classList.add(`note-item`);
    li.dataset.id = i;
    li.appendChild(el);
    return li;
  }

  render() {

    this.el      = this.cloneTemplate();
    this.el.view = this;

    if (this.border) this.el.classList.add(`bordered`);

    if (this.headingLevel !== `h3`) {
      const oldHeading = this.el.querySelector(`.js-notes-list-heading`);
      const newHeading = View.fromHTML(`
        <${ this.headingLevel } class='notes-list-heading js-notes-list-heading'>
          Notes
        </${ this.headingLevel }>
      `);
      oldHeading.replaceWith(newHeading);
    }

    this.renderList();
    this.addEventListeners();

    return this.el;

  }

  renderList() {

    const listOptions = {
      classes:  [`notes`],
      name:     `note`,
      template: this.itemTemplate,
    };

    const listView = new List(this.notes, listOptions);
    const newList = listView.render();
    const oldList = this.el.querySelector(`.notes`);
    oldList.view?.events.stop();
    oldList.replaceWith(newList);

  }

}
<|endoftext|>


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

import './style.scss'

class About extends Component {
  render () {
    return (

          Software Engineer ∙ Open Sourcerer ∙ Tech Enthusiast

          👨🏽💻   😍   🤖

    )
  }
}

export default About<commit_msg>fix: update headline<commit_after>import React, { Component } from "react";

import "./style.scss";

class About extends Component {
  render() {
    return (

          Software Engineer ∙ Open Sourcerer ∙ Tech Enthusiast

              👨🏽💻

              😍

              🤖

    );
  }
}

export default About;
<|endoftext|>