<commit_before>'use strict';

const { CompositeDisposable } = require('atom');
const shell = require('shell');

const SCKConfig = require('./config');
const SassCompiler = require('./sass-compiler');
const PostcssSorting = require('./postcss-sorting.js');
const BottomMessage = require('./bottom-message.js');

const fs = require('fs');
const path = require('path');

exports['default'] = {

    subscriptions: null,
    editorSubscriptions: null,

    config: SCKConfig.options,

    options: {
        sass: null,
        autoprefixer: null
    },

    activate() {

        this.subscriptions = new CompositeDisposable();
        this.editorSubscriptions = new CompositeDisposable();

        this.isProcessing = false;

        this.registerConfigObserveCallback();
        this.registerCommands();
        this.registerTextEditorSaveCallback();
        this.registerContextMenuItem();

    },

    deactivate() {
        this.subscriptions.dispose();
        this.editorSubscriptions.dispose();
    },

    registerConfigObserveCallback() {
        this.subscriptions.add(atom.config.observe('sass-code-kit', value => {
            this.options.sass = SCKConfig.get('sass');
            this.options.autoprefixer = SCKConfig.get('autoprefixer');
        }));
    },

    registerTextEditorSaveCallback() {
        this.editorSubscriptions.add(atom.workspace.observeTextEditors(editor => {
            this.subscriptions.add(editor.onDidSave(event => {
                if(!this.isFileSass(event.path)) {
                    return;
                }
                this.compileFromPath(event.path);
            }));
        }));
    },

    isFileSass(file) {
        return ['.scss', '.sass'].indexOf(path.extname(file)) > -1;
    },

    getProjectPathOfFile(file) {
        var projectPaths = atom.project.getPaths();
        return projectPaths.filter(function(item) {
            return file.startsWith(item);
        }).sort().pop();
    },
    fetchConfig(configFile) {
        if(!fs.existsSync(configFile)) {
            return;
        }
        if(!fs.statSync(configFile).isFile()) {
            return;
        }
        try {
            var config = JSON.parse(fs.readFileSync(configFile));
            if(!config) {
                return;
            }
            return config;
        }
        catch(err) {
            atom.notifications.addError('Parse config error', {
                detail: err.description,
                dismissable: true
            });
        }
        return;
    },
    compile(sassOptions, autoprefixerOptions, file) {
        if(this.isProcessing) {
            return;
        }
        this.compiler = new SassCompiler();
        this.compiler.setupInputFile(file);
        this.compiler.setSassParameters(sassOptions);
        this.compiler.addSassParameter('default-encoding', 'utf-8');
        this.compiler.setPrefixerOptions(autoprefixerOptions);

        this.compiler.onStart(() => {
            // console.log('Compile start');
            this.isProcessing = true;
        });
        this.compiler.onError(message => {
            atom.notifications.addError(`Compile ${message.name} error`, {
                detail: message.error,
                dismissable: true
            });
        });
        this.compiler.onSuccess(message => {
            if(!this.successHint) {
                this.successHint = new BottomMessage();
            }
            var messageContents = document.createElement('div');
            messageContents.innerHTML = `Compile ${message.name} success, output at `;
            var messagePath = document.createElement('a');
            messagePath.innerHTML = message.path;
            messagePath.setAttribute('href', 'javascript');
            messagePath.addEventListener('click', () => {
                shell.showItemInFolder(message.path);
            });
            messageContents.appendChild(messagePath);
            this.successHint.addMessage('success', messageContents);
        });
        this.compiler.onFinished(() => {
            this.isProcessing = false;
            this.compiler.destroy();
            this.compiler = null;

            this.successHint = null;

            var refreshCommand;
            var atomWorkspaceView = atom.views.getView(atom.workspace);
            atom.commands.findCommands({target: atomWorkspaceView}).some(item => {
                if(item.name === 'preview-in-chrome:refresh') {
                    refreshCommand = item;
                    return true;
                }
                return false;
            });

            if(refreshCommand) {
                atom.commands.dispatch(atomWorkspaceView, 'preview-in-chrome:refresh');
            }
            // console.log('Compile finished');
        });
        this.compiler.doCompile();

    },
    compileFromPath(sassPath, manual) {
        var sassOptions = Object.assign({}, this.options.sass);
        var autoprefixerOptions = Object.assign({}, this.options.autoprefixer);
        var projectPath = this.getProjectPathOfFile(sassPath);
        var projectConfig;
        if(projectPath) {
            projectConfig = this.fetchConfig(path.join(projectPath, 'package.json'));
            sassOptions.projectPath = projectPath;
        }
        if(projectConfig) {
            Object.assign(sassOptions, projectConfig.sass);
            Object.assign(autoprefixerOptions, projectConfig.autoprefixer);
        }
        if(sassOptions.compileOnSave || manual) {
            this.compile(sassOptions, autoprefixerOptions, sassPath);
        }
    },
    registerCommands() {
        this.subscriptions.add(atom.commands.add('atom-workspace', {
            'sass-code-kit:sass-compile': event => {
                this.manualCompile(event);
            },
            'sass-code-kit:sass-sorting': () => {
                this.sassSorting();
            },
            'sass-code-kit:insert-cache-buster': () => {
                this.insertCacheBuster(true);
            },
            'sass-code-kit:remove-cache-buster': () => {
                this.insertCacheBuster(false);
            }
        }));
    },
    manualCompile(event) {
        var filePath;
        var target = event.target;
        if(target.matches('.tree-view .file span')) {
            target = target.parentNode;
        }
        if(target.matches('.tree-view .file')) {
            var child = target.firstElementChild;
            filePath = child.getAttribute('data-path');
        }
        else if(atom.workspace.getActiveTextEditor() && atom.workspace.getActiveTextEditor().getPath()) {
            filePath = atom.workspace.getActiveTextEditor().getPath();
        }
        if(filePath) {
            this.compileFromPath(filePath, true);
        }
    },
    sassSorting() {
        if(this.isProcessing) {
            return;
        }
        var editor = atom.workspace.getActiveTextEditor();
        if(!editor || !this.isFileSass(editor.getPath())) {
            return;
        }
        var sortingConfigFile = SCKConfig.get('sorting.sortingRules');
        var sortingOptions = this.fetchConfig(sortingConfigFile);
        this.sorter = new PostcssSorting();
        this.sorter.setContents(editor.getText());
        this.sorter.setOptins(sortingOptions);
        this.sorter.sortingContents();
        this.sorter.onSuccess(result => {
            editor.setText(result.css);
            this.sorter.destroy();
            this.sorter = null;
        });
    },
    registerContextMenuItem() {
        var itemSets = atom.contextMenu.itemSets;
        var contextMenuItem = null;
        itemSets.some(function(itemSet) {
            if(itemSet.selector === '.tree-view .file') {
                return itemSet.items.some(function(item) {
                    if(item.id === 'sass-code-kit-context-menu-compile') {
                        contextMenuItem = item;
                        return true;
                    }
                    return false;
                });
            }
            return false;
        });
        contextMenuItem.shouldDisplay = event => {
            var showItemOption = this.options.sass['showCompileSassItemInTreeViewContextMenu'];
            if(showItemOption) {
                var target = event.target;
                if(target.matches('.tree-view .file span')) {
                    target = target.parentNode;
                }
                if(target.matches('.tree-view .file')) {
                    var child = target.firstElementChild;
                    var filename = child.getAttribute('data-name');
                    return this.isFileSass(filename);
                }
            }
            return false;
        };
    },
    insertCacheBuster(insert) {
        if(this.isProcessing) {
            return;
        }
        var editor = atom.workspace.getActiveTextEditor();
        if(!editor || !this.isFileSass(editor.getPath())) {
            return;
        }
        var reg = new RegExp(/(url\([\'\"]?[^\'\"\)]+\.)(png|gif|jpg|jpeg|svg|webp|ttf|eot|woff)(\?v=[^\'\"\)]+)?([\'\"]?\))/ig);
        editor.setText(editor.getText().replace(reg, function(match, prefix, extname, stamp, suffix) {
            return `${prefix}${extname}${insert?'?v=#{$version}':''}${suffix}`;
        }));
    }
};

module.exports = exports['default']
<commit_msg>add notification in postcss-sorting<commit_after>'use strict';

const { CompositeDisposable } = require('atom');
const shell = require('shell');

const SCKConfig = require('./config');
const SassCompiler = require('./sass-compiler');
const PostcssSorting = require('./postcss-sorting.js');
const BottomMessage = require('./bottom-message.js');

const fs = require('fs');
const path = require('path');

exports['default'] = {

    subscriptions: null,
    editorSubscriptions: null,

    config: SCKConfig.options,

    options: {
        sass: null,
        autoprefixer: null
    },

    activate() {

        this.subscriptions = new CompositeDisposable();
        this.editorSubscriptions = new CompositeDisposable();

        this.isProcessing = false;

        this.registerConfigObserveCallback();
        this.registerCommands();
        this.registerTextEditorSaveCallback();
        this.registerContextMenuItem();

    },

    deactivate() {
        this.subscriptions.dispose();
        this.editorSubscriptions.dispose();
    },

    registerConfigObserveCallback() {
        this.subscriptions.add(atom.config.observe('sass-code-kit', value => {
            this.options.sass = SCKConfig.get('sass');
            this.options.autoprefixer = SCKConfig.get('autoprefixer');
        }));
    },

    registerTextEditorSaveCallback() {
        this.editorSubscriptions.add(atom.workspace.observeTextEditors(editor => {
            this.subscriptions.add(editor.onDidSave(event => {
                if(!this.isFileSass(event.path)) {
                    return;
                }
                this.compileFromPath(event.path);
            }));
        }));
    },

    isFileSass(file) {
        return ['.scss', '.sass'].indexOf(path.extname(file)) > -1;
    },

    getProjectPathOfFile(file) {
        var projectPaths = atom.project.getPaths();
        return projectPaths.filter(function(item) {
            return file.startsWith(item);
        }).sort().pop();
    },
    fetchConfig(configFile) {
        if(!fs.existsSync(configFile)) {
            return;
        }
        if(!fs.statSync(configFile).isFile()) {
            return;
        }
        try {
            var config = JSON.parse(fs.readFileSync(configFile));
            if(!config) {
                return;
            }
            return config;
        }
        catch(err) {
            atom.notifications.addError('Parse config error', {
                detail: err.description,
                dismissable: true
            });
        }
        return;
    },
    compile(sassOptions, autoprefixerOptions, file) {
        if(this.isProcessing) {
            return;
        }
        this.compiler = new SassCompiler();
        this.compiler.setupInputFile(file);
        this.compiler.setSassParameters(sassOptions);
        this.compiler.addSassParameter('default-encoding', 'utf-8');
        this.compiler.setPrefixerOptions(autoprefixerOptions);

        this.compiler.onStart(() => {
            // console.log('Compile start');
            this.isProcessing = true;
        });
        this.compiler.onError(message => {
            atom.notifications.addError(`Compile ${message.name} error`, {
                detail: message.error,
                dismissable: true
            });
        });
        this.compiler.onSuccess(message => {
            if(!this.successHint) {
                this.successHint = new BottomMessage();
            }
            var messageContents = document.createElement('div');
            messageContents.innerHTML = `Compile ${message.name} success, output at `;
            var messagePath = document.createElement('a');
            messagePath.innerHTML = message.path;
            messagePath.setAttribute('href', 'javascript');
            messagePath.addEventListener('click', () => {
                shell.showItemInFolder(message.path);
            });
            messageContents.appendChild(messagePath);
            this.successHint.addMessage('success', messageContents);
        });
        this.compiler.onFinished(() => {
            this.isProcessing = false;
            this.compiler.destroy();
            this.compiler = null;

            this.successHint = null;

            var refreshCommand;
            var atomWorkspaceView = atom.views.getView(atom.workspace);
            atom.commands.findCommands({target: atomWorkspaceView}).some(item => {
                if(item.name === 'preview-in-chrome:refresh') {
                    refreshCommand = item;
                    return true;
                }
                return false;
            });

            if(refreshCommand) {
                atom.commands.dispatch(atomWorkspaceView, 'preview-in-chrome:refresh');
            }
            // console.log('Compile finished');
        });
        this.compiler.doCompile();

    },
    compileFromPath(sassPath, manual) {
        var sassOptions = Object.assign({}, this.options.sass);
        var autoprefixerOptions = Object.assign({}, this.options.autoprefixer);
        var projectPath = this.getProjectPathOfFile(sassPath);
        var projectConfig;
        if(projectPath) {
            projectConfig = this.fetchConfig(path.join(projectPath, 'package.json'));
            sassOptions.projectPath = projectPath;
        }
        if(projectConfig) {
            Object.assign(sassOptions, projectConfig.sass);
            Object.assign(autoprefixerOptions, projectConfig.autoprefixer);
        }
        if(sassOptions.compileOnSave || manual) {
            this.compile(sassOptions, autoprefixerOptions, sassPath);
        }
    },
    registerCommands() {
        this.subscriptions.add(atom.commands.add('atom-workspace', {
            'sass-code-kit:sass-compile': event => {
                this.manualCompile(event);
            },
            'sass-code-kit:sass-sorting': () => {
                this.sassSorting();
            },
            'sass-code-kit:insert-cache-buster': () => {
                this.insertCacheBuster(true);
            },
            'sass-code-kit:remove-cache-buster': () => {
                this.insertCacheBuster(false);
            }
        }));
    },
    manualCompile(event) {
        var filePath;
        var target = event.target;
        if(target.matches('.tree-view .file span')) {
            target = target.parentNode;
        }
        if(target.matches('.tree-view .file')) {
            var child = target.firstElementChild;
            filePath = child.getAttribute('data-path');
        }
        else if(atom.workspace.getActiveTextEditor() && atom.workspace.getActiveTextEditor().getPath()) {
            filePath = atom.workspace.getActiveTextEditor().getPath();
        }
        if(filePath) {
            this.compileFromPath(filePath, true);
        }
    },
    sassSorting() {
        if(this.isProcessing) {
            return;
        }
        var editor = atom.workspace.getActiveTextEditor();
        if(!editor || !this.isFileSass(editor.getPath())) {
            return;
        }
        var sortingConfigFile = SCKConfig.get('sorting.sortingRules');
        var sortingOptions = this.fetchConfig(sortingConfigFile);
        if(!sortingOptions) {
            atom.notifications.addWarning("PostCSS Sorting", {
                detail: "Postcss-sorting requires config file with sorting rules, Make sure 'Sorting rules config file path' in SassCodeKit settings is correct.",
                dismissable: true
            });
            return;
        }
        this.sorter = new PostcssSorting();
        this.sorter.setContents(editor.getText());
        this.sorter.setOptins(sortingOptions);
        this.sorter.sortingContents();
        this.sorter.onSuccess(result => {
            editor.setText(result.css);
            this.sorter.destroy();
            this.sorter = null;
        });
    },
    registerContextMenuItem() {
        var itemSets = atom.contextMenu.itemSets;
        var contextMenuItem = null;
        itemSets.some(function(itemSet) {
            if(itemSet.selector === '.tree-view .file') {
                return itemSet.items.some(function(item) {
                    if(item.id === 'sass-code-kit-context-menu-compile') {
                        contextMenuItem = item;
                        return true;
                    }
                    return false;
                });
            }
            return false;
        });
        contextMenuItem.shouldDisplay = event => {
            var showItemOption = this.options.sass['showCompileSassItemInTreeViewContextMenu'];
            if(showItemOption) {
                var target = event.target;
                if(target.matches('.tree-view .file span')) {
                    target = target.parentNode;
                }
                if(target.matches('.tree-view .file')) {
                    var child = target.firstElementChild;
                    var filename = child.getAttribute('data-name');
                    return this.isFileSass(filename);
                }
            }
            return false;
        };
    },
    insertCacheBuster(insert) {
        if(this.isProcessing) {
            return;
        }
        var editor = atom.workspace.getActiveTextEditor();
        if(!editor || !this.isFileSass(editor.getPath())) {
            return;
        }
        var reg = new RegExp(/(url\([\'\"]?[^\'\"\)]+\.)(png|gif|jpg|jpeg|svg|webp|ttf|eot|woff)(\?v=[^\'\"\)]+)?([\'\"]?\))/ig);
        editor.setText(editor.getText().replace(reg, function(match, prefix, extname, stamp, suffix) {
            return `${prefix}${extname}${insert?'?v=#{$version}':''}${suffix}`;
        }));
    }
};

module.exports = exports['default']
<|endoftext|>


<commit_before>var React   = require('react/addons'),
    scroll  = require('scroll'),
    Beacon  = require('./Beacon'),
    Tooltip = require('./Tooltip');

var joyride = {
    steps: []
};

var Mixin = {
    getInitialState: function () {
        return {
            joyrideCurrentIndex: 0,
            joyrideLocale: {
                close: 'Close',
                last: 'Last',
                next: 'Next'
            },
            joyrideTooltipOffset: 30,
            joyrideScrollToSteps: true,
            joyrideShowOverlay: true,
            joyrideShowTooltip: false,
            joyridePlay: false,
            joyrideType: 'guided',
            joyrideCompleteCallback: undefined,
            joyrideStepCallback: undefined,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        };
    },

    componentDidMount: function () {
        var state = this.state;

        this._target = document.createElement('div');
        this._target.className = 'joyride';
        document.body.appendChild(this._target);

        if (state.joyridePlay && joyride.steps[state.joyrideCurrentIndex]) {
            this._renderLayer();
        }
        window.addEventListener('resize', this._calculatePlacement);
    },

    componentWillUnmount: function () {
        this._unrenderLayer();
        document.body.removeChild(this._target);
        window.removeEventListener('resize', this._calculatePlacement);
    },

    componentDidUpdate: function (prevProps, prevState) {
        var state = this.state,
            opt   = {
                shouldRun: state.joyridePlay && (state.joyridePlay !== prevState.joyridePlay || state.joyrideCurrentIndex !== prevState.joyrideCurrentIndex),
                hasStep: Boolean(joyride.steps[state.joyrideCurrentIndex]),
                hasSteps: joyride.steps.length > 0,
                newX: state.joyrideXPos !== prevState.joyrideXPos,
                newY: state.joyrideYPos !== prevState.joyrideYPos,
                toggleTooltip: state.joyrideShowTooltip !== prevState.joyrideShowTooltip
            };

        if ((opt.shouldRun || opt.toggleTooltip || opt.newX || opt.newY) && opt.hasStep) {
            this._renderLayer();
        }
        else if (opt.shouldRun && opt.hasSteps && !opt.hasStep) {
            if (typeof state.joyrideCompleteCallback === 'function') {
                state.joyrideCompleteCallback(joyride.steps);
            }
            this._unrenderLayer();
        }
    },

    /**
     * Starts the tour
     * @param {boolean} [autorun]- Starts with the first tooltip opened
     */
    joyrideStart: function (autorun) {
        autorun = autorun || false;

        this.setState({
            joyrideShowTooltip: autorun,
            joyridePlay: true
        });
    },

    /**
     * Add Steps
     * @param {object|array} steps - Steps to add to the tour
     * @param {boolean} [start] - Starts the tour right away
     */
    joyrideAddSteps: function (steps, start) {
        var tmpSteps  = [],
            el;

        start = start || this.state.joyridePlay;

        if (Array.isArray(steps)) {
            steps.forEach(function (s) {
                if (s instanceof Object) {
                    tmpSteps.push(s);
                }
            });
        }
        else {
            tmpSteps = [steps];
        }

        tmpSteps.forEach(function (s) {
            el = document.querySelector(s.selector);
            s.position = s.position || 'top';

            if (el && el.offsetParent) {
                joyride.steps.push(s);
            }
        });

        this.setState(
            React.addons.update(this.state, {
                joyridePlay: { $set: start }
            })
        );
    },

    /**
     * Retrieve the current progress of your tour
     * @returns {{index: (number|*), percentageComplete: number, step: (object|null)}}
     */
    joyrideGetProgress: function () {
        return {
            index: this.state.joyrideCurrentIndex,
            percentageComplete: parseFloat(((this.state.joyrideCurrentIndex / joyride.steps.length) * 100).toFixed(2).replace('.00', '')),
            step: joyride.steps[this.state.joyrideCurrentIndex]
        };
    },

    _onClickBeacon: function (e) {
        e.preventDefault();

        this.setState({
            joyrideShowTooltip: true,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        });
    },

    _onClickTooltip: function (e) {
        e.preventDefault();
        e.stopPropagation();

        var state = this.state,
            type  = e.currentTarget.dataset.type;

        if (typeof state.joyrideStepCallback === 'function') {
            state.joyrideStepCallback(joyride.steps[state.joyrideCurrentIndex]);
        }

        this.setState({
            joyrideShowTooltip: type === 'button' && state.joyrideType === 'guided' && Boolean(joyride.steps[state.joyrideCurrentIndex + 1]),
            joyrideCurrentIndex: state.joyrideCurrentIndex + 1,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        }, function () {
            this._scrollToNextStep();
        }.bind(this));
    },

    _calculatePlacement: function () {
        var state     = this.state,
            step      = joyride.steps[state.joyrideCurrentIndex],
            position,
            body,
            target,
            component = document.querySelector((state.joyrideShowTooltip ? '.joyride-tooltip' : '.joyride-beacon')),
            placement = {
                x: -1000,
                y: -1000
            };

        if (step && (!component.classList.contains('animate') || state.joyrideXPos < 0)) {
            position = step.position;
            body = document.body.getBoundingClientRect();
            target = document.querySelector(step.selector).getBoundingClientRect();
            component = component.getBoundingClientRect();

            if (window.innerWidth < 768) {
                if (/^left/.test(position)) {
                    position = 'top';
                }
                else if (/^right/.test(position)) {
                    position = 'bottom';
                }
            }

            // Calculate x position
            if (/^left/.test(position)) {
                placement.x = target.left - (state.joyrideShowTooltip ? component.width + state.joyrideTooltipOffset : component.width / 2);
            }
            else if (/^right/.test(position)) {
                placement.x = target.left + target.width - (state.joyrideShowTooltip ? -state.joyrideTooltipOffset : component.width / 2);
            }
            else {
                placement.x = target.left + target.width / 2 - component.width / 2;
            }

            // Calculate y position
            if (/^top/.test(position)) {
                placement.y = (target.top - body.top) - (state.joyrideShowTooltip ? component.height + state.joyrideTooltipOffset : component.height / 2);
            }
            else if (/^bottom/.test(position)) {
                placement.y = (target.top - body.top) + target.height - (state.joyrideShowTooltip ? -state.joyrideTooltipOffset : component.height / 2);
            }
            else {
                placement.y = (target.top - body.top) + target.height / 2 - component.height / 2 + (state.joyrideShowTooltip ? state.joyrideTooltipOffset : 0);
            }

            if (/^bottom|^top/.test(position)) {
                if (/left/.test(position)) {
                    placement.x = target.left - (state.joyrideShowTooltip ? state.joyrideTooltipOffset : component.width / 2);
                }
                else if (/right/.test(position)) {
                    placement.x = target.left + target.width - (state.joyrideShowTooltip ? component.width - state.joyrideTooltipOffset : component.width / 2);
                }
            }

            this.setState({
                joyrideXPos: this._preventWindowOverflow(Math.ceil(placement.x), 'x', component.width, component.height),
                joyrideYPos: this._preventWindowOverflow(Math.ceil(placement.y), 'y', component.width, component.height)
            });
        }
    },

    _preventWindowOverflow: function (value, axis, elWidth, elHeight) {
        var winWidth = window.innerWidth;
        var docHeight = document.body.offsetHeight;

        if (axis.toLowerCase() === 'x') {
            if (value + elWidth > winWidth) {
                value = winWidth - elWidth - 10;
            }
            else if (value < 0) {
                value = 10;
            }
        }
        else if (axis.toLowerCase() === 'y') {
            if (value + elHeight > docHeight) {
                value = docHeight - elHeight - 10;
            }
            else if (value < 0) {
                value = 10;
            }
        }

        return value;
    },

    _getPosition: function () {
        var step = joyride.steps[this.state.joyrideCurrentIndex],
            position = step.position,
            elPosition = document.querySelector(step.selector).getBoundingClientRect().top - document.body.getBoundingClientRect().top;

        if (window.innerWidth < 768) {
            if (/^left/.test(position)) {
                position = 'top';
            }
            else if (/^right/.test(position)) {
                position = 'bottom';
            }
        }

        if (/^top/.test(position)) {
            position = this.state.joyrideYPos - 20;
        }
        else if (/^bottom|^left|^right/.test(position)) {
            position = elPosition - 20;
        }

        return position;
    },

    _scrollToNextStep: function (cb) {
        if (joyride.steps[this.state.joyrideCurrentIndex] && this.state.joyrideScrollToSteps) {
            scroll.top(document.body, this._getPosition(), cb);
        }
    },

    _renderLayer: function () {
        React.render(this._renderCurrentStep(), this._target);
        this._calculatePlacement();
    },

    _unrenderLayer: function () {
        React.unmountComponentAtNode(this._target);
    },

    _renderCurrentStep: function () {
        var state       = this.state,
            component,
            currentStep = joyride.steps[state.joyrideCurrentIndex],
            hasNextStep = Boolean(joyride.steps[state.joyrideCurrentIndex + 1]),
            buttonText  = state.joyrideType === 'guided' ? (hasNextStep ? (state.joyrideLocale.next + ' ' + (state.joyrideCurrentIndex + 1) + '/' + joyride.steps.length) : state.joyrideLocale.last) : state.joyrideLocale.close,
            target      = currentStep && currentStep.selector ? document.querySelector(currentStep.selector) : null,
            cssPosition = target ? target.style.position : null;

        if (target) {
            if (state.joyrideShowTooltip) {
                component = React.createElement(Tooltip, {
                    animate: state.joyrideXPos > -1,
                    cssPosition: cssPosition,
                    xPos: state.joyrideXPos,
                    yPos: state.joyrideYPos,
                    step: currentStep,
                    buttonText: buttonText,
                    showOverlay: state.joyrideShowOverlay,
                    onClick: this._onClickTooltip
                });
            }
            else {
                component = React.createElement(Beacon, {
                    cssPosition: cssPosition,
                    xPos: state.joyrideXPos,
                    yPos: state.joyrideYPos,
                    handleClick: this._onClickBeacon
                });
            }
        }

        return component;
    }
};

module.exports = Mixin;
<commit_msg>change steps count optional<commit_after>var React   = require('react/addons'),
    scroll  = require('scroll'),
    Beacon  = require('./Beacon'),
    Tooltip = require('./Tooltip');

var joyride = {
    steps: []
};

var Mixin = {
    getInitialState: function () {
        return {
            joyrideCurrentIndex: 0,
            joyrideLocale: {
                close: 'Close',
                last: 'Last',
                next: 'Next'
            },
            joyrideTooltipOffset: 30,
            joyrideScrollToSteps: true,
            joyrideShowOverlay: true,
            joyrideShowStepsCount: false,
            joyrideShowTooltip: false,
            joyridePlay: false,
            joyrideType: 'guided',
            joyrideCompleteCallback: undefined,
            joyrideStepCallback: undefined,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        };
    },

    componentDidMount: function () {
        var state = this.state;

        this._target = document.createElement('div');
        this._target.className = 'joyride';
        document.body.appendChild(this._target);

        if (state.joyridePlay && joyride.steps[state.joyrideCurrentIndex]) {
            this._renderLayer();
        }
        window.addEventListener('resize', this._calculatePlacement);
    },

    componentWillUnmount: function () {
        this._unrenderLayer();
        document.body.removeChild(this._target);
        window.removeEventListener('resize', this._calculatePlacement);
    },

    componentDidUpdate: function (prevProps, prevState) {
        var state = this.state,
            opt   = {
                shouldRun: state.joyridePlay && (state.joyridePlay !== prevState.joyridePlay || state.joyrideCurrentIndex !== prevState.joyrideCurrentIndex),
                hasStep: Boolean(joyride.steps[state.joyrideCurrentIndex]),
                hasSteps: joyride.steps.length > 0,
                newX: state.joyrideXPos !== prevState.joyrideXPos,
                newY: state.joyrideYPos !== prevState.joyrideYPos,
                toggleTooltip: state.joyrideShowTooltip !== prevState.joyrideShowTooltip
            };

        if ((opt.shouldRun || opt.toggleTooltip || opt.newX || opt.newY) && opt.hasStep) {
            this._renderLayer();
        }
        else if (opt.shouldRun && opt.hasSteps && !opt.hasStep) {
            if (typeof state.joyrideCompleteCallback === 'function') {
                state.joyrideCompleteCallback(joyride.steps);
            }
            this._unrenderLayer();
        }
    },

    /**
     * Starts the tour
     * @param {boolean} [autorun]- Starts with the first tooltip opened
     */
    joyrideStart: function (autorun) {
        autorun = autorun || false;

        this.setState({
            joyrideShowTooltip: autorun,
            joyridePlay: true
        });
    },

    /**
     * Add Steps
     * @param {object|array} steps - Steps to add to the tour
     * @param {boolean} [start] - Starts the tour right away
     */
    joyrideAddSteps: function (steps, start) {
        var tmpSteps  = [],
            el;

        start = start || this.state.joyridePlay;

        if (Array.isArray(steps)) {
            steps.forEach(function (s) {
                if (s instanceof Object) {
                    tmpSteps.push(s);
                }
            });
        }
        else {
            tmpSteps = [steps];
        }

        tmpSteps.forEach(function (s) {
            el = document.querySelector(s.selector);
            s.position = s.position || 'top';

            if (el && el.offsetParent) {
                joyride.steps.push(s);
            }
        });

        this.setState(
            React.addons.update(this.state, {
                joyridePlay: { $set: start }
            })
        );
    },

    /**
     * Retrieve the current progress of your tour
     * @returns {{index: (number|*), percentageComplete: number, step: (object|null)}}
     */
    joyrideGetProgress: function () {
        return {
            index: this.state.joyrideCurrentIndex,
            percentageComplete: parseFloat(((this.state.joyrideCurrentIndex / joyride.steps.length) * 100).toFixed(2).replace('.00', '')),
            step: joyride.steps[this.state.joyrideCurrentIndex]
        };
    },

    _onClickBeacon: function (e) {
        e.preventDefault();

        this.setState({
            joyrideShowTooltip: true,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        });
    },

    _onClickTooltip: function (e) {
        e.preventDefault();
        e.stopPropagation();

        var state = this.state,
            type  = e.currentTarget.dataset.type;

        if (typeof state.joyrideStepCallback === 'function') {
            state.joyrideStepCallback(joyride.steps[state.joyrideCurrentIndex]);
        }

        this.setState({
            joyrideShowTooltip: type === 'button' && state.joyrideType === 'guided' && Boolean(joyride.steps[state.joyrideCurrentIndex + 1]),
            joyrideCurrentIndex: state.joyrideCurrentIndex + 1,
            joyrideXPos: -1000,
            joyrideYPos: -1000
        }, function () {
            this._scrollToNextStep();
        }.bind(this));
    },

    _calculatePlacement: function () {
        var state     = this.state,
            step      = joyride.steps[state.joyrideCurrentIndex],
            position,
            body,
            target,
            component = document.querySelector((state.joyrideShowTooltip ? '.joyride-tooltip' : '.joyride-beacon')),
            placement = {
                x: -1000,
                y: -1000
            };

        if (step && (!component.classList.contains('animate') || state.joyrideXPos < 0)) {
            position = step.position;
            body = document.body.getBoundingClientRect();
            target = document.querySelector(step.selector).getBoundingClientRect();
            component = component.getBoundingClientRect();

            if (window.innerWidth < 768) {
                if (/^left/.test(position)) {
                    position = 'top';
                }
                else if (/^right/.test(position)) {
                    position = 'bottom';
                }
            }

            // Calculate x position
            if (/^left/.test(position)) {
                placement.x = target.left - (state.joyrideShowTooltip ? component.width + state.joyrideTooltipOffset : component.width / 2);
            }
            else if (/^right/.test(position)) {
                placement.x = target.left + target.width - (state.joyrideShowTooltip ? -state.joyrideTooltipOffset : component.width / 2);
            }
            else {
                placement.x = target.left + target.width / 2 - component.width / 2;
            }

            // Calculate y position
            if (/^top/.test(position)) {
                placement.y = (target.top - body.top) - (state.joyrideShowTooltip ? component.height + state.joyrideTooltipOffset : component.height / 2);
            }
            else if (/^bottom/.test(position)) {
                placement.y = (target.top - body.top) + target.height - (state.joyrideShowTooltip ? -state.joyrideTooltipOffset : component.height / 2);
            }
            else {
                placement.y = (target.top - body.top) + target.height / 2 - component.height / 2 + (state.joyrideShowTooltip ? state.joyrideTooltipOffset : 0);
            }

            if (/^bottom|^top/.test(position)) {
                if (/left/.test(position)) {
                    placement.x = target.left - (state.joyrideShowTooltip ? state.joyrideTooltipOffset : component.width / 2);
                }
                else if (/right/.test(position)) {
                    placement.x = target.left + target.width - (state.joyrideShowTooltip ? component.width - state.joyrideTooltipOffset : component.width / 2);
                }
            }

            this.setState({
                joyrideXPos: this._preventWindowOverflow(Math.ceil(placement.x), 'x', component.width, component.height),
                joyrideYPos: this._preventWindowOverflow(Math.ceil(placement.y), 'y', component.width, component.height)
            });
        }
    },

    _preventWindowOverflow: function (value, axis, elWidth, elHeight) {
        var winWidth = window.innerWidth;
        var docHeight = document.body.offsetHeight;

        if (axis.toLowerCase() === 'x') {
            if (value + elWidth > winWidth) {
                value = winWidth - elWidth - 10;
            }
            else if (value < 0) {
                value = 10;
            }
        }
        else if (axis.toLowerCase() === 'y') {
            if (value + elHeight > docHeight) {
                value = docHeight - elHeight - 10;
            }
            else if (value < 0) {
                value = 10;
            }
        }

        return value;
    },

    _getPosition: function () {
        var step = joyride.steps[this.state.joyrideCurrentIndex],
            position = step.position,
            elPosition = document.querySelector(step.selector).getBoundingClientRect().top - document.body.getBoundingClientRect().top;

        if (window.innerWidth < 768) {
            if (/^left/.test(position)) {
                position = 'top';
            }
            else if (/^right/.test(position)) {
                position = 'bottom';
            }
        }

        if (/^top/.test(position)) {
            position = this.state.joyrideYPos - 20;
        }
        else if (/^bottom|^left|^right/.test(position)) {
            position = elPosition - 20;
        }

        return position;
    },

    _scrollToNextStep: function (cb) {
        if (joyride.steps[this.state.joyrideCurrentIndex] && this.state.joyrideScrollToSteps) {
            scroll.top(document.body, this._getPosition(), cb);
        }
    },

    _renderLayer: function () {
        React.render(this._renderCurrentStep(), this._target);
        this._calculatePlacement();
    },

    _unrenderLayer: function () {
        React.unmountComponentAtNode(this._target);
    },

    _renderCurrentStep: function () {
        var state       = this.state,
            component,
            currentStep = joyride.steps[state.joyrideCurrentIndex],
            hasNextStep = Boolean(joyride.steps[state.joyrideCurrentIndex + 1]),
            buttonText  = state.joyrideType === 'guided' ? (hasNextStep ? (state.joyrideLocale.next + (state.joyrideShowStepsCount ? ' ' + (state.joyrideCurrentIndex + 1) + '/' + joyride.steps.length : '')) : state.joyrideLocale.last) : state.joyrideLocale.close,
            target      = currentStep && currentStep.selector ? document.querySelector(currentStep.selector) : null,
            cssPosition = target ? target.style.position : null;

        if (target) {
            if (state.joyrideShowTooltip) {
                component = React.createElement(Tooltip, {
                    animate: state.joyrideXPos > -1,
                    cssPosition: cssPosition,
                    xPos: state.joyrideXPos,
                    yPos: state.joyrideYPos,
                    step: currentStep,
                    buttonText: buttonText,
                    showOverlay: state.joyrideShowOverlay,
                    onClick: this._onClickTooltip
                });
            }
            else {
                component = React.createElement(Beacon, {
                    cssPosition: cssPosition,
                    xPos: state.joyrideXPos,
                    yPos: state.joyrideYPos,
                    handleClick: this._onClickBeacon
                });
            }
        }

        return component;
    }
};

module.exports = Mixin;
<|endoftext|>


<commit_before>var dnode  = require('dnode'),
    fs     = require('fs'),
    log4js = require('log4js'),
    util   = require('util'),
    _      = require('underscore'),
    config = require('./config.js'),
    Batch  = require('./batch.js');
var logger  = log4js.getLogger('appserver');

function Client(parent, sock) {
  this.parent = parent;
  this.socket = sock;
  this.pending_search = null;
  this.last_search = null;
  this.active_search = null;
  this.remote_address = sock.handshake.address;
}

Client.prototype.debug = function() {
  logger.debug("[%s:%d] %s",
               this.remote_address.address,
               this.remote_address.port,
               util.format.apply(null, arguments));
}

Client.prototype.new_search = function (line, file, id) {
  this.debug('new search: (%s) (%j)', id, {line:line, file:file});
  if (this.last_search &&
      line === this.last_search.line &&
      file === this.last_search.file)
    return;
  this.pending_search = {
    line: line,
    file: file,
    id: id
  };
  this.dispatch_search();
}

Client.prototype.search_done = function() {
  this.active_search = null;
  process.nextTick(this.dispatch_search.bind(this));
}

Client.prototype.dispatch_search = function() {
  if (this.pending_search !== null &&
      !this.active_search &&
      this.parent.remotes.length) {
    var codesearch = this.parent.remotes.pop();
    console.assert(codesearch.cs_ready);
    var start = new Date();
    this.last_search = this.pending_search;
    this.debug('dispatching: (%j)...', this.pending_search);

    var search = this.pending_search;
    this.pending_search = null;
    this.active_search  = search;
    var self   = this;
    var sock   = this.socket;
    var batch  = new Batch(function (m) {
                             sock.emit('match', search.id, m);
                           }, 50);
    var cbs = {
      not_ready: function() {
        logger.info('Remote reports not ready for %j', search);
        if (self.pending_search === null)
          self.pending_search = search;
        self.search_done();
        codesearch.cs_client = null;
      },
      error: function (err) {
        sock.emit('regex_error', search.id, err);
        self.search_done();
        codesearch.cs_client = null;
      },
      match: function (match) {
        match = JSON.parse(match);
        logger.trace("Reporting match %j for %j.", match, search);
        batch.send(match);
      },
      done: function (stats) {
        stats = JSON.parse(stats);
        var time = (new Date()) - start;
        batch.flush();
        sock.emit('search_done', search.id, time, stats.why);
        self.debug("Search done: (%j): %s", search, time);
        self.search_done();
        codesearch.cs_client = null;
      }
    }
    codesearch.try_search(search.line, search.file, cbs);
    codesearch.cs_ready = false;
    codesearch.cs_client = this;
  }
}

function SearchServer(config, io) {
  var parent = this;
  this.config  = config;
  this.remotes = [];
  this.connections = [];
  this.clients = {};

  config.BACKENDS.forEach(
    function (bk) {
      for (var i = 0; i < config.BACKEND_CONNECTIONS; i++) {
        (function() {
           var remote = null;
           var connection = null;

           function ready() {
             logger.debug('Remote ready!');
             if (remote.cs_ready) {
               logger.debug('(already queued)!');
               return;
             }
             remote.cs_ready = true;
             parent.remotes.push(remote);
             parent.dispatch();
           }

           function disconnected() {
             logger.info("Lost connection to backend")
             parent.remotes = parent.remotes.filter(function (r) {return r !== remote});
             parent.connections = parent.connections.filter(
               function (c) { return c !== connection});
             if (remote.cs_client)
               remote.cs_client.search_done();
           }

           dnode({ ready: ready }).
             connect(
               bk[0], bk[1],
               function (r, conn) {
                 parent.connections.push(conn);
                 remote = r;
                 connection = conn;
                 remote.cs_ready  = false;
                 remote.cs_client = null;
                 logger.info("Connected to codesearch daemon.");
                 conn.on('ready',     ready);
                 conn.on('reconnect', ready);
                 conn.on('close',     disconnected);
                 conn.on('end',       disconnected);
               }, {
                 reconnect: 200
               });
         })();
      }
    });

  var Server = function (sock) {
    logger.info("New client [%j]", sock.handshake.address);
    parent.clients[sock.id] = new Client(parent, sock);
    sock.on('new_search', function(line, file, id) {
              if (id == null)
                id = line;
              parent.clients[sock.id].new_search(line, file, id);
    });
    sock.on('disconnect', function() {
              delete parent.clients[sock.id];
            });
  };

  io.sockets.on('connection', function(sock) {
    new Server(sock);
  });
}

SearchServer.prototype.dispatch = function () {
  var clients = this.clients;
  _.shuffle(_.values(this.clients)).forEach(
    function (client) {
      client.dispatch_search();
    });
}

module.exports = SearchServer;
<commit_msg>Log client connection/disconnection.<commit_after>var dnode  = require('dnode'),
    fs     = require('fs'),
    log4js = require('log4js'),
    util   = require('util'),
    _      = require('underscore'),
    config = require('./config.js'),
    Batch  = require('./batch.js');
var logger  = log4js.getLogger('appserver');

function Client(parent, sock) {
  this.parent = parent;
  this.socket = sock;
  this.pending_search = null;
  this.last_search = null;
  this.active_search = null;
  this.remote_address = sock.handshake.address;
}

Client.prototype.debug = function() {
  logger.debug("[%s:%d] %s",
               this.remote_address.address,
               this.remote_address.port,
               util.format.apply(null, arguments));
}

Client.prototype.new_search = function (line, file, id) {
  this.debug('new search: (%s) (%j)', id, {line:line, file:file});
  if (this.last_search &&
      line === this.last_search.line &&
      file === this.last_search.file)
    return;
  this.pending_search = {
    line: line,
    file: file,
    id: id
  };
  this.dispatch_search();
}

Client.prototype.search_done = function() {
  this.active_search = null;
  process.nextTick(this.dispatch_search.bind(this));
}

Client.prototype.dispatch_search = function() {
  if (this.pending_search !== null &&
      !this.active_search &&
      this.parent.remotes.length) {
    var codesearch = this.parent.remotes.pop();
    console.assert(codesearch.cs_ready);
    var start = new Date();
    this.last_search = this.pending_search;
    this.debug('dispatching: (%j)...', this.pending_search);

    var search = this.pending_search;
    this.pending_search = null;
    this.active_search  = search;
    var self   = this;
    var sock   = this.socket;
    var batch  = new Batch(function (m) {
                             sock.emit('match', search.id, m);
                           }, 50);
    var cbs = {
      not_ready: function() {
        logger.info('Remote reports not ready for %j', search);
        if (self.pending_search === null)
          self.pending_search = search;
        self.search_done();
        codesearch.cs_client = null;
      },
      error: function (err) {
        sock.emit('regex_error', search.id, err);
        self.search_done();
        codesearch.cs_client = null;
      },
      match: function (match) {
        match = JSON.parse(match);
        logger.trace("Reporting match %j for %j.", match, search);
        batch.send(match);
      },
      done: function (stats) {
        stats = JSON.parse(stats);
        var time = (new Date()) - start;
        batch.flush();
        sock.emit('search_done', search.id, time, stats.why);
        self.debug("Search done: (%j): %s", search, time);
        self.search_done();
        codesearch.cs_client = null;
      }
    }
    codesearch.try_search(search.line, search.file, cbs);
    codesearch.cs_ready = false;
    codesearch.cs_client = this;
  }
}

function SearchServer(config, io) {
  var parent = this;
  this.config  = config;
  this.remotes = [];
  this.connections = [];
  this.clients = {};

  config.BACKENDS.forEach(
    function (bk) {
      for (var i = 0; i < config.BACKEND_CONNECTIONS; i++) {
        (function() {
           var remote = null;
           var connection = null;

           function ready() {
             logger.debug('Remote ready!');
             if (remote.cs_ready) {
               logger.debug('(already queued)!');
               return;
             }
             remote.cs_ready = true;
             parent.remotes.push(remote);
             parent.dispatch();
           }

           function disconnected() {
             logger.info("Lost connection to backend")
             parent.remotes = parent.remotes.filter(function (r) {return r !== remote});
             parent.connections = parent.connections.filter(
               function (c) { return c !== connection});
             if (remote.cs_client)
               remote.cs_client.search_done();
           }

           dnode({ ready: ready }).
             connect(
               bk[0], bk[1],
               function (r, conn) {
                 parent.connections.push(conn);
                 remote = r;
                 connection = conn;
                 remote.cs_ready  = false;
                 remote.cs_client = null;
                 logger.info("Connected to codesearch daemon.");
                 conn.on('ready',     ready);
                 conn.on('reconnect', ready);
                 conn.on('close',     disconnected);
                 conn.on('end',       disconnected);
               }, {
                 reconnect: 200
               });
         })();
      }
    });

  var Server = function (sock) {
    logger.info("New client (%s)[%j]", sock.id, sock.handshake.address);
    parent.clients[sock.id] = new Client(parent, sock);
    sock.on('new_search', function(line, file, id) {
              if (id == null)
                id = line;
              parent.clients[sock.id].new_search(line, file, id);
    });
    sock.on('disconnect', function() {
              logger.info("Disconnected (%d)[%j]", sock.id, sock.handshake.address);
              delete parent.clients[sock.id];
            });
  };

  io.sockets.on('connection', function(sock) {
    new Server(sock);
  });
}

SearchServer.prototype.dispatch = function () {
  var clients = this.clients;
  _.shuffle(_.values(this.clients)).forEach(
    function (client) {
      client.dispatch_search();
    });
}

module.exports = SearchServer;
<|endoftext|>


<commit_before>require(`babel-register`);
const crypto = require(`crypto`);
const fs = require(`fs`);
const path = require(`path`);
const webpack = require(`webpack`);
const swPrecache = require(`sw-precache`);
const htmlMinifier = require(`html-minifier`);
const ExtractTextPlugin = require(`extract-text-webpack-plugin`);
const rimraf = require(`rimraf`);

const config = require(`./config.shared.js`);
const appHtml = require(`../app/server/appHtml`).default;

console.time(`build`);

rimraf.sync(path.resolve(__dirname, `../public`));

fs.mkdirSync(`./public`);

fs.createReadStream(`./assets/favicon.ico`).pipe(fs.createWriteStream(`./public/favicon.ico`));

config.context = __dirname;
// in dev mode, only one package is produced
// in prod, an 'app' and an 'app-with-polyfills' is produced
config.entry = {
  app: [
    path.resolve(__dirname, `../app/client/client.js`),
  ],
  'app-with-polyfills': [
    `babel-polyfill`,
    `whatwg-fetch`,
    path.resolve(__dirname, `../app/client/client.js`),
  ],
};

config.plugins = config.plugins.concat([
  new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: JSON.stringify(`production`),
    },
  }),
  new webpack.optimize.UglifyJsPlugin({ sourceMap: true }),
  new webpack.optimize.DedupePlugin(),
]);

config.plugins.push(
  new ExtractTextPlugin(`styles.css`)
);

config.module.loaders.push({
  test: /\.jsx?$/,
  exclude: /node_modules/,
  loader: `babel`,
  query: {
    compact: true,
  },
});

config.module.loaders.push({
  test: /\.scss$/,
  exclude: /node_modules/,
  loader: ExtractTextPlugin.extract(`style`, `css!sass`), // I hate you so much webpack
});

function generateServiceWorker() {
  swPrecache.write(path.resolve(__dirname, `../public/service-worker.js`), {
    cacheId: `know-it-all`,
    filename: `service-worker.js`,
    stripPrefix: `public/`,
    staticFileGlobs: [
      `public/app.*.js`, // don't include the polyfills version
      `public/*.{html,ico,json}`,
    ],
    // skipWaiting: true,
    dontCacheBustUrlsMatching: [
      /\.(js|json)$/, // I'm cache busting js and json files myself
    ],
  });

  console.timeEnd(`build`);
}

const compiler = webpack(config);

compiler.run((compileError, stats) => {
  if (compileError) {
    console.error(compileError);
    return;
  }

  const jsonStats = stats.toJson({
    modules: false,
    chunks: false,
  });

  console.log(stats.toString({
    chunks: false,
    colors: true,
  }));

  // get the name of the JS file (including the hash webpack created)
  const scriptFileName = jsonStats.assetsByChunkName.app[0];

  // get the hash for the data file and copy it to public
  fs.readFile(`./app/data/data.json`, `utf8`, (dataFileError, dataJson) => {
    const dataHash = crypto.createHash(`md5`).update(dataJson).digest(`hex`);
    const dataFileName = `data.${dataHash}.json`;

    // generate the html with the correct paths
    const htmlString = appHtml({
      dataFileName,
      scriptFileName,
      mode: `production`,
    });

    // This does pretty much nothing ATM (17.5kb -> 17.4kb)
    const minHtmlString = htmlMinifier.minify(htmlString, {
      caseSensitive: true,
      keepClosingSlash: true,
      minifyCSS: true,
      minifyJS: true,
    });

    let dataFileWritten = false;
    let indexFileWritten = false;
    // write the data to a new file with the hash in the name
    // this could be a copy/rename but we already
    // had the file loaded and perf doesn't matter here
    // when they're both done, generate the service worker
    fs.writeFile(`./public/${dataFileName}`, dataJson, `utf8`, (dataJsonError) => {
      if (dataJsonError) console.error(`Error writing data.json to disk: ${dataJsonError}`);

      if (indexFileWritten) generateServiceWorker();
      dataFileWritten = true;
    });

    fs.writeFile(`./public/index.html`, minHtmlString, `utf8`, (indexError) => {
      if (indexError) console.error(`Error creating index.html: ${indexError}`);

      if (dataFileWritten) generateServiceWorker();
      indexFileWritten = true;
    });
  });
});
<commit_msg>Turn on skip waiting for service worker<commit_after>require(`babel-register`);
const crypto = require(`crypto`);
const fs = require(`fs`);
const path = require(`path`);
const webpack = require(`webpack`);
const swPrecache = require(`sw-precache`);
const htmlMinifier = require(`html-minifier`);
const ExtractTextPlugin = require(`extract-text-webpack-plugin`);
const rimraf = require(`rimraf`);

const config = require(`./config.shared.js`);
const appHtml = require(`../app/server/appHtml`).default;

console.time(`build`);

rimraf.sync(path.resolve(__dirname, `../public`));

fs.mkdirSync(`./public`);

fs.createReadStream(`./assets/favicon.ico`).pipe(fs.createWriteStream(`./public/favicon.ico`));

config.context = __dirname;
// in dev mode, only one package is produced
// in prod, an 'app' and an 'app-with-polyfills' is produced
config.entry = {
  app: [
    path.resolve(__dirname, `../app/client/client.js`),
  ],
  'app-with-polyfills': [
    `babel-polyfill`,
    `whatwg-fetch`,
    path.resolve(__dirname, `../app/client/client.js`),
  ],
};

config.plugins = config.plugins.concat([
  new webpack.DefinePlugin({
    'process.env': {
      NODE_ENV: JSON.stringify(`production`),
    },
  }),
  new webpack.optimize.UglifyJsPlugin({ sourceMap: true }),
  new webpack.optimize.DedupePlugin(),
]);

config.plugins.push(
  new ExtractTextPlugin(`styles.css`)
);

config.module.loaders.push({
  test: /\.jsx?$/,
  exclude: /node_modules/,
  loader: `babel`,
  query: {
    compact: true,
  },
});

config.module.loaders.push({
  test: /\.scss$/,
  exclude: /node_modules/,
  loader: ExtractTextPlugin.extract(`style`, `css!sass`), // I hate you so much webpack
});

function generateServiceWorker() {
  swPrecache.write(path.resolve(__dirname, `../public/service-worker.js`), {
    cacheId: `know-it-all`,
    filename: `service-worker.js`,
    stripPrefix: `public/`,
    staticFileGlobs: [
      `public/app.*.js`, // don't include the polyfills version
      `public/*.{html,ico,json}`,
    ],
    dontCacheBustUrlsMatching: [
      /\.(js|json)$/, // I'm cache busting js and json files myself
    ],
    skipWaiting: true,
  });

  console.timeEnd(`build`);
}

const compiler = webpack(config);

compiler.run((compileError, stats) => {
  if (compileError) {
    console.error(compileError);
    return;
  }

  const jsonStats = stats.toJson({
    modules: false,
    chunks: false,
  });

  console.log(stats.toString({
    chunks: false,
    colors: true,
  }));

  // get the name of the JS file (including the hash webpack created)
  const scriptFileName = jsonStats.assetsByChunkName.app[0];

  // get the hash for the data file and copy it to public
  fs.readFile(`./app/data/data.json`, `utf8`, (dataFileError, dataJson) => {
    const dataHash = crypto.createHash(`md5`).update(dataJson).digest(`hex`);
    const dataFileName = `data.${dataHash}.json`;

    // generate the html with the correct paths
    const htmlString = appHtml({
      dataFileName,
      scriptFileName,
      mode: `production`,
    });

    // This does pretty much nothing ATM (17.5kb -> 17.4kb)
    const minHtmlString = htmlMinifier.minify(htmlString, {
      caseSensitive: true,
      keepClosingSlash: true,
      minifyCSS: true,
      minifyJS: true,
    });

    let dataFileWritten = false;
    let indexFileWritten = false;
    // write the data to a new file with the hash in the name
    // this could be a copy/rename but we already
    // had the file loaded and perf doesn't matter here
    // when they're both done, generate the service worker
    fs.writeFile(`./public/${dataFileName}`, dataJson, `utf8`, (dataJsonError) => {
      if (dataJsonError) console.error(`Error writing data.json to disk: ${dataJsonError}`);

      if (indexFileWritten) generateServiceWorker();
      dataFileWritten = true;
    });

    fs.writeFile(`./public/index.html`, minHtmlString, `utf8`, (indexError) => {
      if (indexError) console.error(`Error creating index.html: ${indexError}`);

      if (dataFileWritten) generateServiceWorker();
      indexFileWritten = true;
    });
  });
});
<|endoftext|>


<commit_before>/* Module contains Cloud Commander SS(Server Side) objects.
 *
 * List of objects:
 * - Cache
 * - Minify
 */

/*
 * Обьект для работы с кэшем
 * аналог клиентского обьекта
 * с тем отличием, что в нём
 * будут храниться серверные
 * данные, такие как файлы
 * отдаваемые клиенту
 * (файлы проэкта по большому
 * счёту, для ускорения
 * первичной загрузки)
 */
exports.Cache={
    /* приватный переключатель возможности работы с кэшем */
    _allowed            :true,
    /* данные в которых храняться файлы
     * в формате <поле> : <значение>
     * _data[name]=pData;
     * одному имени соответствуют
     * одни данные
     */
    _data               :{},

    /* функция говорит можно ли работать с кэшем */
    isAllowed           :(function(){
        return this._allowed;
        }),
    /* функция устанавливает возможность работать с кэшем */
    setAllowed          :(function(pAllowed){
        this._allowed=pAllowed;
    }),
    /* Если доступен кэш
     * сохраняем в него данные
     */
    set                  :(function(pName, pData){
        if(this._allowed && pName && pData){
            this._data[pName]=pData;
        }
    }),
    /* Если доступен Cache принимаем из него данные*/
    get                 :(function(pName){
        if(this._allowed && pName){
            return this._data[pName];
        }
        else return null;
    }),

    /* Функция очищает кэш*/
    clear               :(function(){
        if(this._allowed){
            this._data={};
        }
    })
};

/* Обьект для сжатия скриптов и стилей
 * по умолчанию - сжимаються
 */
exports.Minify={
    /* pathes to directories */
    INDEX           :'index.html',
    /* приватный переключатель минимизации */
    _allowed               :{css:true,js:true,html:true, img: true},

    /* функция разрешает или
     * запрещает минимизировать
     * css/js/html
     * @pAllowed: - структура, в которой
     *              передаються параметры
     *              минификации, вида
     *              {js:true,css:true,html:false; img:true}
     * img отвечает за перевод картинок в base64
     * и сохранение их в css-файл
     */
    setAllowed              :(function(pAllowed){
       if(pAllowed){
           this._allowed.css=pAllowed.css;
           this._allowed.js=pAllowed.js;
           this._allowed.html=pAllowed.html;
           this._allowed.img=pAllowed.img;
       }
    }),

    /*
     * Функция минимизирует css/js/html
     * если установлены параметры минимизации
     */
    doit                    :(function(){
        if(this._allowed.css ||
            this._allowed.js ||
            this._allowed.html){
                var lMinify;
                try{
                    lMinify      = require('minify');
                }catch(pError){
                    this._allowed={js:false,css:false,html:false};
                    return console.log('Could not minify '  +
                        'withou minify module\n'            +
                        'for fixing type:\n'                +
                        'git submodule init\n'              +
                        'git submodule update\n'            +
                        'or\n'                              +
                        'npm i minify');
                }
                /*
                 * temporary changed dir path,
                 * becouse directory lib is write
                 * protected by others by default
                 * so if node process is started
                 * from other user (root for example
                 * in nodester) we can not write
                 * minified versions
                 */
                this.MinFolder = '/' + lMinify.MinFolder;
                var lMinFolder=this.MinFolder;

                /* post processing function for file
                 * client.js
                 */

                /* если мы сжимаем client.js -
                 * меняем строку cloudfunc.js на
                 * cloudfunc.min.js и выводим сообщение
                 *
                 * меняем строку keyBinding.js на
                 * keyBinding.min.js
                 * если другой файл - ничего не деалем
                 */
                var lPostProcessing_f = function(pFinalCode){
                    console.log('file name of ' +
                        'cloudfunc.js'          +
                        ' in '                  +
                        'client.js'             +
                        ' changed. size:',
                    (pFinalCode = pFinalCode
                        .replace('cloudfunc.js','cloudfunc.min.js')
                            .replace('keyBinding.js','keyBinding.min.js')
                                .replace('/lib/', lMinFolder)
                                    .replace('/lib/client/',
                                        lMinFolder)).length);
                    return pFinalCode;
                };

                var lOptimizeParams;

                if (this._allowed.js) {
                    lOptimizeParams=[{
                        'client.js': lPostProcessing_f},
                        'lib/cloudfunc.js',
                        'lib/client/keyBinding.js'];
                }

                if (this._allowed.html)
                    lOptimizeParams[lOptimizeParams.length] = this.INDEX;

                if (this._allowed.css) {
                    lOptimizeParams[lOptimizeParams.length] = {
                        './css/style.css' : this._allowed.img };

                    lOptimizeParams[lOptimizeParams.length] = {
                        './css/reset.css': this._allowed.img};
                }

                if (lOptimizeParams)
                    lMinify.optimize(lOptimizeParams);

                this.Cache = lMinify.Cache;
        }
    }),

    /* minification folder name */
    MinFolder   :'',
    Cache       :{}
};<commit_msg>fixed bug with initialization of lOptimizeParams<commit_after>/* Module contains Cloud Commander SS(Server Side) objects.
 *
 * List of objects:
 * - Cache
 * - Minify
 */

/*
 * Обьект для работы с кэшем
 * аналог клиентского обьекта
 * с тем отличием, что в нём
 * будут храниться серверные
 * данные, такие как файлы
 * отдаваемые клиенту
 * (файлы проэкта по большому
 * счёту, для ускорения
 * первичной загрузки)
 */
exports.Cache={
    /* приватный переключатель возможности работы с кэшем */
    _allowed            :true,
    /* данные в которых храняться файлы
     * в формате <поле> : <значение>
     * _data[name]=pData;
     * одному имени соответствуют
     * одни данные
     */
    _data               :{},

    /* функция говорит можно ли работать с кэшем */
    isAllowed           :(function(){
        return this._allowed;
        }),
    /* функция устанавливает возможность работать с кэшем */
    setAllowed          :(function(pAllowed){
        this._allowed=pAllowed;
    }),
    /* Если доступен кэш
     * сохраняем в него данные
     */
    set                  :(function(pName, pData){
        if(this._allowed && pName && pData){
            this._data[pName]=pData;
        }
    }),
    /* Если доступен Cache принимаем из него данные*/
    get                 :(function(pName){
        if(this._allowed && pName){
            return this._data[pName];
        }
        else return null;
    }),

    /* Функция очищает кэш*/
    clear               :(function(){
        if(this._allowed){
            this._data={};
        }
    })
};

/* Обьект для сжатия скриптов и стилей
 * по умолчанию - сжимаються
 */
exports.Minify={
    /* pathes to directories */
    INDEX           :'index.html',
    /* приватный переключатель минимизации */
    _allowed               :{css:true,js:true,html:true, img: true},

    /* функция разрешает или
     * запрещает минимизировать
     * css/js/html
     * @pAllowed: - структура, в которой
     *              передаються параметры
     *              минификации, вида
     *              {js:true,css:true,html:false; img:true}
     * img отвечает за перевод картинок в base64
     * и сохранение их в css-файл
     */
    setAllowed              :(function(pAllowed){
       if(pAllowed){
           this._allowed=pAllowed;
       }
    }),

    /*
     * Функция минимизирует css/js/html
     * если установлены параметры минимизации
     */
    doit                    :(function(){
        if(this._allowed.css ||
            this._allowed.js ||
            this._allowed.html){
                var lMinify;
                try{
                    lMinify      = require('minify');
                }catch(pError){
                    this._allowed={js:false,css:false,html:false};
                    return console.log('Could not minify '  +
                        'withou minify module\n'            +
                        'for fixing type:\n'                +
                        'git submodule init\n'              +
                        'git submodule update\n'            +
                        'or\n'                              +
                        'npm i minify');
                }
                /*
                 * temporary changed dir path,
                 * becouse directory lib is write
                 * protected by others by default
                 * so if node process is started
                 * from other user (root for example
                 * in nodester) we can not write
                 * minified versions
                 */
                this.MinFolder = '/' + lMinify.MinFolder;
                var lMinFolder=this.MinFolder;

                /* post processing function for file
                 * client.js
                 */

                /* если мы сжимаем client.js -
                 * меняем строку cloudfunc.js на
                 * cloudfunc.min.js и выводим сообщение
                 *
                 * меняем строку keyBinding.js на
                 * keyBinding.min.js
                 * если другой файл - ничего не деалем
                 */
                var lPostProcessing_f = function(pFinalCode){
                    console.log('file name of ' +
                        'cloudfunc.js'          +
                        ' in '                  +
                        'client.js'             +
                        ' changed. size:',
                    (pFinalCode = pFinalCode
                        .replace('cloudfunc.js','cloudfunc.min.js')
                            .replace('keyBinding.js','keyBinding.min.js')
                                .replace('/lib/', lMinFolder)
                                    .replace('/lib/client/',
                                        lMinFolder)).length);
                    return pFinalCode;
                };

                var lOptimizeParams = [];

                if (this._allowed.js) {
                    lOptimizeParams=[{
                        'client.js': lPostProcessing_f},
                        'lib/cloudfunc.js',
                        'lib/client/keyBinding.js'];
                }

                if (this._allowed.html)
                    lOptimizeParams[lOptimizeParams.length] = this.INDEX;

                if (this._allowed.css) {
                    lOptimizeParams[lOptimizeParams.length] = {
                        './css/style.css' : this._allowed.img };

                    lOptimizeParams[lOptimizeParams.length] = {
                        './css/reset.css': this._allowed.img};
                }

                if (lOptimizeParams)
                    lMinify.optimize(lOptimizeParams);

                this.Cache = lMinify.Cache;
        }
    }),

    /* minification folder name */
    MinFolder   :'',
    Cache       :{}
};<|endoftext|>


<commit_before>/* module make possible connectoin thru socket.io on a server */

(function() {
    'use strict';

    var main            = global.cloudcmd.main,
        SRVDIR          = main.SRVDIR,

        io              = main.require('socket.io'),
        update          = main.srvrequire('update'),
        exec            = main.child_process.exec,
        Util            = main.util,
        mainpackage     = main.mainpackage,
        CLOUDCMD        = mainpackage.name,

        ClientFuncs     = [],
        OnMessageFuncs  = [],
        INFO_LOG_LEVEL  = 2,
        WIN32           = main.WIN32,

        /* windows commands thet require
         * unicode charset on locales
         * different then English
         */
        Win32Commands   = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL',
                            'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS',
                            'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY',
                            'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY',
                            'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND',
                            'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL',
                            'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE',
                            'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT',
                            'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME',
                            'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT',
                            'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE',
                            'VER', 'VERIFY', 'VOL', 'XCOPY'];

    /**
     * function listen on servers port
     * @pServer {Object} started server object
     */
    exports.listen = function(pServer) {
        var lRet, lListen, lConnNum, lMsg, lConn_func;

        if (io) {
            lListen     = io.listen(pServer),
            lConnNum    = 0;

            lListen.set('log level', INFO_LOG_LEVEL);

            /*
             * on Win7 application is crashing,
             * when options below is used.
             *
             * https://github.com/LearnBoost/socket.io/issues/1314
             *
             */
            if (!WIN32) {
                lListen.enable('browser client minification');
                lListen.enable('browser client gzip');
                lListen.enable('browser client etag');
            }

            lListen.set('transports', [
              'websocket',
              'htmlfile',
              'xhr-polling',
              'jsonp-polling'
            ]);

            lRet = lListen.sockets.on('connection', function (socket) {
                var lJSON;
                ++lConnNum;

                if(!OnMessageFuncs[lConnNum]) {
                    lMsg    = log(lConnNum, 'connected\n');
                    lJSON   = {
                        stdout : lMsg
                    };
                    lMsg    = Util.stringifyJSON(lJSON);

                    socket.send(lMsg);

                    OnMessageFuncs[lConnNum]    = onMessage(lConnNum, socket);
                    lConn_func                  = OnMessageFuncs[lConnNum];

                    socket.on('message', lConn_func);
                    socket.on('disconnect', Util.call(disconnect, lConnNum));
                } else {
                    lMsg = log(lConnNum, ' in use. Reconnecting...');
                    socket.send('{"stdout":"' + lMsg + '"}');
                    socket.disconnect();
                }

            });
        }

        return lRet;
    };

    function disconnect(pParams) {
        var lConnNum, lRet = Util.checkObj(pParams, ['params']);

        if(lRet) {
            lConnNum = pParams.params;
            OnMessageFuncs  [lConnNum]  =
            ClientFuncs     [lConnNum]  = null;

            log(lConnNum, 'disconnected');
        }

    }

    /**
     * function gets onMessage function
     * that execute needed command
     *
     * @param pConnNum, pSocket
     */
    function onMessage(pConnNum, pSocket) {
        return function(pCommand) {
            var lMsg, lJSON, lWinCommand, lExec_func, lDir,
                lHome, lError, lRet, lExecSymbols;

            Util.log('#' + pConnNum + ': ' + pCommand);

            if (Util.isContainStrAtBegin(pCommand, CLOUDCMD)) {
                pCommand = Util.removeStr(pCommand, CLOUDCMD);

                if( Util.isContainStrAtBegin(pCommand, ' ') ) {
                    pCommand = Util.removeStr(pCommand, ' ');

                    if( Util.isContainStrAtBegin(pCommand, 'update') && update )
                        update.get();

                    if( Util.strCmp(pCommand, 'exit') )
                        if(WIN32)
                            pCommand = 'taskkill -f /PID ' + process.pid;
                        else
                            pCommand = 'kill -9 ' + process.pid;
                }
                else {
                    lJSON = {
                        stdout  : CLOUDCMD + ' exit     \n' +
                                  CLOUDCMD + ' update   \n',
                        stderr  : null
                    };

                    lMsg = Util.stringifyJSON( lJSON );
                    pSocket.send(lMsg);

                    Util.log('received from client: ' + pCommand);
                    Util.log('sended to client: ' + lMsg);
                }
            } else if( Util.isContainStrAtBegin(pCommand, 'cd ') ) {
                lDir    = Util.removeStr(pCommand, 'cd ');
                lHome   = process.env.HOME;

                if ( Util.isContainStr(lDir, '~') )
                    lDir = Util.replaceStr(lDir, '~', lHome);

                lError = Util.tryCatchLog(function() {
                    process.chdir(lDir);
                });

                if (!lError)
                    lRet = true;
                else {
                    lJSON = {
                        stderr: Util.stringifyJSON( lError )
                    };

                    lMsg = Util.stringifyJSON( lJSON );

                    pSocket.send(lMsg);
                }
            }

            if (!lRet) {
                /* if we on windows and command is build in
                 * change code page to unicode becouse
                 * windows use unicode on non English versions
                 */
                if(WIN32) {
                    lWinCommand = pCommand.toUpperCase();

                    if( Win32Commands.indexOf(lWinCommand) >= 0 )
                        pCommand = 'chcp 65001 |' + pCommand;
                }

                if(!ClientFuncs[pConnNum])
                    ClientFuncs[pConnNum] = getExec(pSocket, pConnNum);

                lExec_func = ClientFuncs[pConnNum];

                lExecSymbols = ['&', '{', '}', '|', '\'', '"'];

                if (Util.isContainStr(pCommand, lExecSymbols))
                    exec(pCommand, lExec_func);
                else
                    getSpawn(pSocket, pConnNum, pCommand);
            }
        };
    }

    /**
     * function send result of command to client
     * @param pSocket
     */
    function getExec(pSocket, pConnNum) {
        return function(pError, pStdout, pStderr) {
            var lErrorStr, lExecStr, lExec,
                lError          = pStderr || pError;

            if (lError)
                if ( Util.isString(lError) )
                    lErrorStr   = lError;
                else
                    lErrorStr   = Util.stringifyJSON( lError );

            lExec               = {
                stdout : pStdout,
                stderr : lError
            };

            lExecStr       = JSON.stringify(lExec);

            log(pConnNum, pError, 'error');
            log(pConnNum, pStderr, 'stderror');

            pSocket.send(lExecStr);
        };
    }

    function getSpawn(pSocket, pConnNum, pCommand) {
        var send, cmd, spawn,
            args    = pCommand.split(' ');

        pCommand    = args.shift();

        spawn       = main.child_process.spawn;
        cmd         = spawn(pCommand, args);
        send        = function(data, isError) {
            var lExecStr,
                lExec               = {},
                msg                 = data.toString();

            if (isError)
                lExec.stderr    = msg;
            else
                lExec.stdout    = msg;

            lExecStr            = JSON.stringify(lExec);
            pSocket.send(lExecStr);
            Util.log(msg);
        };

        cmd.stdout.on('data', send);

        cmd.stderr.on('data', function(data) {
            send(data, true);
        });

        cmd.on('error', Util.retFalse);
    }

    function log(pConnNum, pStr, pType) {
        var lRet, lType = ' ';

        if (pStr) {

            if (pType)
                lType += pType + ':';

            lRet = 'client #' + pConnNum + lType + pStr;

            Util.log(lRet);
        }

        return lRet;
    }
})();
<commit_msg>refactor(socket) getExec<commit_after>/* module make possible connectoin thru socket.io on a server */

(function() {
    'use strict';

    var main            = global.cloudcmd.main,
        SRVDIR          = main.SRVDIR,

        io              = main.require('socket.io'),
        update          = main.srvrequire('update'),
        exec            = main.child_process.exec,
        Util            = main.util,
        mainpackage     = main.mainpackage,
        CLOUDCMD        = mainpackage.name,

        ClientFuncs     = [],
        OnMessageFuncs  = [],
        INFO_LOG_LEVEL  = 2,
        WIN32           = main.WIN32,

        /* windows commands thet require
         * unicode charset on locales
         * different then English
         */
        Win32Commands   = ['ASSOC', 'AT', 'ATTRIB', 'BREAK', 'CACLS', 'CALL',
                            'CD', 'CHCP', 'CHDIR', 'CHKDSK', 'CHKNTFS', 'CLS',
                            'CMD', 'COLOR', 'COMP', 'COMPACT', 'CONVERT', 'COPY',
                            'DATE', 'DEL', 'DIR', 'DISKCOMP', 'DISKCOPY', 'DOSKEY',
                            'ECHO', 'ENDLOCAL', 'ERASE', 'EXIT', 'FC', 'FIND',
                            'FINDSTR', 'FOR', 'FORMAT', 'FTYPE', 'GOTO', 'GRAFTABL',
                            'HELP', 'IF', 'LABEL', 'MD', 'MKDIR', 'MODE', 'MORE',
                            'MOVE', 'PATH', 'PAUSE', 'POPD', 'PRINT', 'PROMPT',
                            'PUSHD', 'RD', 'RECOVER', 'REM', 'REN', 'RENAME',
                            'REPLACE', 'RMDIR', 'SET', 'SETLOCAL', 'SHIFT', 'SORT',
                            'START', 'SUBST', 'TIME', 'TITLE', 'TREE', 'TYPE',
                            'VER', 'VERIFY', 'VOL', 'XCOPY'];

    /**
     * function listen on servers port
     * @pServer {Object} started server object
     */
    exports.listen = function(pServer) {
        var lRet, lListen, lConnNum, lMsg, lConn_func;

        if (io) {
            lListen     = io.listen(pServer),
            lConnNum    = 0;

            lListen.set('log level', INFO_LOG_LEVEL);

            /*
             * on Win7 application is crashing,
             * when options below is used.
             *
             * https://github.com/LearnBoost/socket.io/issues/1314
             *
             */
            if (!WIN32) {
                lListen.enable('browser client minification');
                lListen.enable('browser client gzip');
                lListen.enable('browser client etag');
            }

            lListen.set('transports', [
              'websocket',
              'htmlfile',
              'xhr-polling',
              'jsonp-polling'
            ]);

            lRet = lListen.sockets.on('connection', function (socket) {
                var lJSON;
                ++lConnNum;

                if(!OnMessageFuncs[lConnNum]) {
                    lMsg    = log(lConnNum, 'connected\n');
                    lJSON   = {
                        stdout : lMsg
                    };
                    lMsg    = Util.stringifyJSON(lJSON);

                    socket.send(lMsg);

                    OnMessageFuncs[lConnNum]    = onMessage(lConnNum, socket);
                    lConn_func                  = OnMessageFuncs[lConnNum];

                    socket.on('message', lConn_func);
                    socket.on('disconnect', Util.call(disconnect, lConnNum));
                } else {
                    lMsg = log(lConnNum, ' in use. Reconnecting...');
                    socket.send('{"stdout":"' + lMsg + '"}');
                    socket.disconnect();
                }

            });
        }

        return lRet;
    };

    function disconnect(pParams) {
        var lConnNum, lRet = Util.checkObj(pParams, ['params']);

        if(lRet) {
            lConnNum = pParams.params;
            OnMessageFuncs  [lConnNum]  =
            ClientFuncs     [lConnNum]  = null;

            log(lConnNum, 'disconnected');
        }

    }

    /**
     * function gets onMessage function
     * that execute needed command
     *
     * @param pConnNum, pSocket
     */
    function onMessage(pConnNum, pSocket) {
        return function(pCommand) {
            var lMsg, lJSON, lWinCommand, lExec_func, lDir,
                lHome, lError, lRet, lExecSymbols;

            Util.log('#' + pConnNum + ': ' + pCommand);

            if (Util.isContainStrAtBegin(pCommand, CLOUDCMD)) {
                pCommand = Util.removeStr(pCommand, CLOUDCMD);

                if( Util.isContainStrAtBegin(pCommand, ' ') ) {
                    pCommand = Util.removeStr(pCommand, ' ');

                    if( Util.isContainStrAtBegin(pCommand, 'update') && update )
                        update.get();

                    if( Util.strCmp(pCommand, 'exit') )
                        if(WIN32)
                            pCommand = 'taskkill -f /PID ' + process.pid;
                        else
                            pCommand = 'kill -9 ' + process.pid;
                }
                else {
                    lJSON = {
                        stdout  : CLOUDCMD + ' exit     \n' +
                                  CLOUDCMD + ' update   \n',
                        stderr  : null
                    };

                    lMsg = Util.stringifyJSON( lJSON );
                    pSocket.send(lMsg);

                    Util.log('received from client: ' + pCommand);
                    Util.log('sended to client: ' + lMsg);
                }
            } else if( Util.isContainStrAtBegin(pCommand, 'cd ') ) {
                lDir    = Util.removeStr(pCommand, 'cd ');
                lHome   = process.env.HOME;

                if ( Util.isContainStr(lDir, '~') )
                    lDir = Util.replaceStr(lDir, '~', lHome);

                lError = Util.tryCatchLog(function() {
                    process.chdir(lDir);
                });

                if (!lError)
                    lRet = true;
                else {
                    lJSON = {
                        stderr: Util.stringifyJSON( lError )
                    };

                    lMsg = Util.stringifyJSON( lJSON );

                    pSocket.send(lMsg);
                }
            }

            if (!lRet) {
                /* if we on windows and command is build in
                 * change code page to unicode becouse
                 * windows use unicode on non English versions
                 */
                if(WIN32) {
                    lWinCommand = pCommand.toUpperCase();

                    if( Win32Commands.indexOf(lWinCommand) >= 0 )
                        pCommand = 'chcp 65001 |' + pCommand;
                }

                if(!ClientFuncs[pConnNum])
                    ClientFuncs[pConnNum] = getExec(pSocket, pConnNum);

                lExec_func = ClientFuncs[pConnNum];

                lExecSymbols = ['&', '{', '}', '|', '\'', '"'];

                if (Util.isContainStr(pCommand, lExecSymbols))
                    exec(pCommand, lExec_func);
                else
                    getSpawn(pSocket, pConnNum, pCommand);
            }
        };
    }

    /**
     * function send result of command to client
     * @param pSocket
     */
    function getExec(pSocket, pConnNum) {
        return function(pError, pStdout, pStderr) {
            var lErrorStr, lExecStr, lExec,
                lError          = pError || pStderr;

            if (!Util.isString(lError))
                lErrorStr   = lError.toString() + '\n';

            lExec               = {
                stdout : pStdout,
                stderr : lErrorStr || lError
            };

            lExecStr       = JSON.stringify(lExec);

            log(pConnNum, pError, 'error');
            log(pConnNum, pStderr, 'stderror');

            pSocket.send(lExecStr);
        };
    }

    function getSpawn(pSocket, pConnNum, pCommand) {
        var send, cmd, spawn,
            args    = pCommand.split(' ');

        pCommand    = args.shift();

        spawn       = main.child_process.spawn;
        cmd         = spawn(pCommand, args);
        send        = function(data, isError) {
            var lExecStr,
                lExec               = {},
                msg                 = data.toString();

            if (isError)
                lExec.stderr    = msg;
            else
                lExec.stdout    = msg;

            lExecStr            = JSON.stringify(lExec);
            pSocket.send(lExecStr);
            Util.log(msg);
        };

        cmd.stdout.on('data', send);

        cmd.stderr.on('data', function(data) {
            send(data, true);
        });

        cmd.on('error', Util.retFalse);
    }

    function log(pConnNum, pStr, pType) {
        var lRet, lType = ' ';

        if (pStr) {

            if (pType)
                lType += pType + ':';

            lRet = 'client #' + pConnNum + lType + pStr;

            Util.log(lRet);
        }

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


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
*/

(function() {

    var cordova = require('cordova');
    var exec = require('cordova/exec');
    var modulemapper = require('cordova/modulemapper');
    var urlutil = require('cordova/urlutil');
    var channel = require('cordova/channel');

    function AdView() {
        this.id = 0;
        this.onClose = function () {};
    }

    AdView.prototype = {
        close: function () {
            this.id && exec(null, null, "NativeAd", "close", [this.id]);
        }
    };

    var NativeAd = {
        ads: {},
        onClose: function (id) {
            var ad = this.ads[id];
            if (ad) ad.onClose();
        },
        openUrl: function(strUrl, strWindowName, strWindowFeatures, callbacks) {
            // Don't catch calls that write to existing frames (e.g. named iframes).
            if (window.frames && window.frames[strWindowName]) {
                var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
                return origOpenFunc.apply(window, arguments);
            }
            strUrl = urlutil.makeAbsolute(strUrl);
            callbacks = callbacks || {};
            var cb = function (eventname) {
                console.log('open ad landing page');
                callbacks(eventname);
            };
            strWindowFeatures = strWindowFeatures || "";
            exec(cb, cb, "NativeAd", "openUrl", [strUrl, strWindowName, strWindowFeatures]);
        },
        open: function(html, width, height, showAt, closeAt) {
            var ad = new AdView();
            width = width || 0;
            height = height || 0;
            showAt = showAt || 'bottom';
            closeAt = closeAt || 'topRight';
            var cb = function (id) {
                console.log('open ad', id);
                ad.id = id;
                NativeAd.ads[id] = ad;
            };
            exec(cb, cb, "NativeAd", "open", [html, width, height, showAt, closeAt]);
            return ad;
        }
    };

    if (cordova.platformId == 'android') {
        channel.onCordovaReady.subscribe(function () {
            exec(function (id) {
                id && NativeAd.onClose(id);
            }, null, "NativeAd", "onClose", []);
        });
    }

    module.exports = NativeAd;

})();
<commit_msg>bugfix<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 *
*/

(function() {

    var cordova = require('cordova');
    var exec = require('cordova/exec');
    var modulemapper = require('cordova/modulemapper');
    var urlutil = require('cordova/urlutil');
    var channel = require('cordova/channel');

    function AdView() {
        this.id = 0;
        this.onClose = function () {};
    }

    AdView.prototype = {
        close: function () {
            this.id && exec(null, null, "NativeAd", "close", [this.id]);
        }
    };

    var NativeAd = {
        ads: {},
        onClose: function (id) {
            var ad = this.ads[id];
            if (ad) ad.onClose();
        },
        openUrl: function(strUrl, strWindowName, strWindowFeatures, callbacks) {
            // Don't catch calls that write to existing frames (e.g. named iframes).
            if (window.frames && window.frames[strWindowName]) {
                var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
                return origOpenFunc.apply(window, arguments);
            }
            strUrl = urlutil.makeAbsolute(strUrl);
            callbacks = callbacks || {};
            var cb = function (eventname) {
                console.log('open ad landing page');
                // callbacks(eventname);
            };
            strWindowFeatures = strWindowFeatures || "";
            exec(cb, cb, "NativeAd", "openUrl", [strUrl, strWindowName, strWindowFeatures]);
        },
        open: function(html, width, height, showAt, closeAt) {
            var ad = new AdView();
            width = width || 0;
            height = height || 0;
            showAt = showAt || 'bottom';
            closeAt = closeAt || 'topRight';
            var cb = function (id) {
                console.log('open ad', id);
                ad.id = id;
                NativeAd.ads[id] = ad;
            };
            exec(cb, cb, "NativeAd", "open", [html, width, height, showAt, closeAt]);
            return ad;
        }
    };

    if (cordova.platformId == 'android') {
        channel.onCordovaReady.subscribe(function () {
            exec(function (id) {
                id && NativeAd.onClose(id);
            }, null, "NativeAd", "onClose", []);
        });
    }

    module.exports = NativeAd;

})();
<|endoftext|>