<commit_before>/*!
 * jQuery UI Button @VERSION
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Button
//>>group: Widgets
//>>description: Enhances a form with themeable buttons.
//>>docs: http://api.jqueryui.com/button/
//>>demos: http://jqueryui.com/button/
//>>css.structure: ../themes/base/core.css
//>>css.structure: ../themes/base/button.css
//>>css.theme: ../themes/base/theme.css

( function( factory ) {
	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define( [
			"jquery",

			// These are only for backcompat
			// TODO: Remove after 1.12
			"./controlgroup",
			"./checkboxradio",

			"../keycode",
			"../widget"
		], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
}( function( $ ) {

$.widget( "ui.button", {
	version: "@VERSION",
	defaultElement: "<button>",
	options: {
		classes: {
			"ui-button": "ui-corner-all"
		},
		disabled: null,
		icon: null,
		iconPosition: "beginning",
		label: null,
		showLabel: true
	},

	_getCreateOptions: function() {
		var disabled,

			// This is to support cases like in jQuery Mobile where the base widget does have
			// an implementation of _getCreateOptions
			options = this._super() || {};

		this.isInput = this.element.is( "input" );

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}

		this.originalLabel = this.isInput ? this.element.val() : this.element.html();
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		return options;
	},

	_create: function() {
		if ( !this.option.showLabel & !this.options.icon ) {
			this.options.showLabel = true;
		}

		// We have to check the option again here even though we did in _getCreateOptions,
		// because null may have been passed on init which would override what was set in
		// _getCreateOptions
		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled || false;
		}

		this.hasTitle = !!this.element.attr( "title" );

		// Check to see if the label needs to be set or if its already correct
		if ( this.options.label && this.options.label !== this.originalLabel ) {
			if ( this.isInput ) {
				this.element.val( this.options.label );
			} else {
				this.element.html( this.options.label );
			}
		}
		this._addClass( "ui-button", "ui-widget" );
		this._setOption( "disabled", this.options.disabled );
		this._enhance();

		if ( this.element.is( "a" ) ) {
			this._on( {
				"keyup": function( event ) {
					if ( event.keyCode === $.ui.keyCode.SPACE ) {
						event.preventDefault();

						// Support: PhantomJS <= 1.9, IE 8 Only
						// If a native click is available use it so we actually cause navigation
						// otherwise just trigger a click event
						if ( this.element[ 0 ].click ) {
							this.element[ 0 ].click();
						} else {
							this.element.trigger( "click" );
						}
					}
				}
			} );
		}
	},

	_enhance: function() {
		this.element.attr( "role", "button" );

		if ( this.options.icon ) {
			this._updateIcon( "icon", this.options.icon );
			this._updateTooltip();
		}
	},

	_updateTooltip: function() {
		this.title = this.element.attr( "title" );

		if ( !this.options.showLabel && !this.title ) {
			this.element.attr( "title", this.options.label );
		}
	},

	_updateIcon: function( option, value ) {
		var icon = option !== "iconPosition",
			position = icon ? this.options.iconPosition : value,
			displayBlock = position === "top" || position === "bottom";

		// Create icon
		if ( !this.icon ) {
			this.icon = $( "<span>" );

			this._addClass( this.icon, "ui-button-icon", "ui-icon" );

			if ( !this.options.showLabel ) {
				this._addClass( "ui-button-icon-only" );
			}
		} else if ( icon ) {

			// If we are updating the icon remove the old icon class
			this._removeClass( this.icon, null, this.options.icon );
		}

		// If we are updating the icon add the new icon class
		if ( icon ) {
			this._addClass( this.icon, null, value );
		}

		this._attachIcon( position );

		// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
		// the iconSpace if there is one.
		if ( displayBlock ) {
			this._addClass( this.icon, null, "ui-widget-icon-block" );
			if ( this.iconSpace ) {
				this.iconSpace.remove();
			}
		} else {

			// Position is beginning or end so remove the ui-widget-icon-block class and add the
			// space if it does not exist
			if ( !this.iconSpace ) {
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-button-icon-space" );
			}
			this._removeClass( this.icon, null, "ui-wiget-icon-block" );
			this._attachIconSpace( position );
		}
	},

	_destroy: function() {
		this.element.removeAttr( "role" );

		if ( this.icon ) {
			this.icon.remove();
		}
		if ( this.iconSpace ) {
			this.iconSpace.remove();
		}
		if ( !this.hasTitle ) {
			this.element.removeAttr( "title" );
		}
	},

	_attachIconSpace: function( iconPosition ) {
		this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
	},

	_attachIcon: function( iconPosition ) {
		this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
	},

	_setOptions: function( options ) {
		var newShowLabel = options.showLabel === undefined ?
				this.options.showLabel :
				options.showLabel,
			newIcon = options.icon === undefined ? this.options.icon : options.icon;

		if ( !newShowLabel && !newIcon ) {
			options.showLabel = true;
		}
		this._super( options );
	},

	_setOption: function( key, value ) {
		if ( key === "icon" ) {
			if ( value ) {
				this._updateIcon( key, value );
			} else if ( this.icon ) {
				this.icon.remove();
				if ( this.iconSpace ) {
					this.iconSpace.remove();
				}
			}
		}

		if ( key === "iconPosition" ) {
			this._updateIcon( key, value );
		}

		// Make sure we can't end up with a button that has neither text nor icon
		if ( key === "showLabel" ) {
				this._toggleClass( this._classes( "ui-button-icon-only" ), null, !value );
				this._updateTooltip();
		}

		if ( key === "label" ) {
			if ( this.isInput ) {
				this.element.val( value );
			} else {

				// If there is an icon, append it, else nothing then append the value
				// this avoids removal of the icon when setting label text
				this.element.html( value );
				if ( this.icon ) {
					this._attachIcon( this.options.iconPosition );
					this._attachIconSpace( this.options.iconPosition );
				}
			}
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;
			if ( value ) {
				this.element.blur();
			}
		}
	},

	refresh: function() {

		// Make sure to only check disabled if its an element that supports this otherwise
		// check for the disabled class to determine state
		var isDisabled = this.element.is( "input, button" ) ?
			this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { disabled: isDisabled } );
		}

		this._updateTooltip();
	}
} );

// DEPRECATED
if ( $.uiBackCompat !== false ) {

	// Text and Icons options
	$.widget( "ui.button", $.ui.button, {
		options: {
			text: true,
			icons: {
				primary: null,
				secondary: null
			}
		},

		_create: function() {
			if ( this.options.showLabel && !this.options.text ) {
				this.options.showLabel = this.options.text;
			}
			if ( !this.options.showLabel && this.options.text ) {
				this.options.text = this.options.showLabel;
			}
			if ( !this.options.icon && ( this.options.icons.primary ||
					this.options.icons.secondary ) ) {
				if ( this.options.icons.primary ) {
					this.options.icon = this.options.icons.primary;
				} else {
					this.options.icon = this.options.icons.secondary;
					this.options.iconPosition = "end";
				}
			} else if ( this.options.icon ) {
				this.options.icons.primary = this.options.icon;
			}
			this._super();
		},

		_setOption: function( key, value ) {
			if ( key === "text" ) {
				this._super( "showLabel", value );
				return;
			}
			if ( key === "showLabel" ) {
				this.options.text = value;
			}
			if ( key === "icon" ) {
				this.options.icons.primary = value;
			}
			if ( key === "icons" ) {
				if ( value.primary ) {
					this._super( "icon", value.primary );
					this._super( "iconPosition", "beginning" );
				} else if ( value.secondary ) {
					this._super( "icon", value.secondary );
					this._super( "iconPosition", "end" );
				}
			}
			this._superApply( arguments );
		}
	} );

	$.fn.button = ( function( orig ) {
		return function() {
			if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
					( this.length && this[ 0 ].tagName === "INPUT" && (
						this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
					) ) ) {
				return orig.apply( this, arguments );
			}
			if ( !$.ui.checkboxradio ) {
				$.error( "Checkboxradio widget missing" );
			}
			if ( arguments.length === 0 ) {
				return this.checkboxradio( {
					"icon": false
				} );
			}
			return this.checkboxradio.apply( this, arguments );
		};
	} )( $.fn.button );

	$.fn.buttonset = function() {
		if ( !$.ui.controlgroup ) {
			$.error( "Controlgroup widget missing" );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
			return this.controlgroup.apply( this,
				[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
			return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
		}
		if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
			arguments[ 0 ].items = {
				button: arguments[ 0 ].items
			};
		}
		return this.controlgroup.apply( this, arguments );
	};
}

return $.ui.button;

} ) );
<commit_msg>Button: Typos<commit_after>/*!
 * jQuery UI Button @VERSION
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Button
//>>group: Widgets
//>>description: Enhances a form with themeable buttons.
//>>docs: http://api.jqueryui.com/button/
//>>demos: http://jqueryui.com/button/
//>>css.structure: ../themes/base/core.css
//>>css.structure: ../themes/base/button.css
//>>css.theme: ../themes/base/theme.css

( function( factory ) {
	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define( [
			"jquery",

			// These are only for backcompat
			// TODO: Remove after 1.12
			"./controlgroup",
			"./checkboxradio",

			"../keycode",
			"../widget"
		], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
}( function( $ ) {

$.widget( "ui.button", {
	version: "@VERSION",
	defaultElement: "<button>",
	options: {
		classes: {
			"ui-button": "ui-corner-all"
		},
		disabled: null,
		icon: null,
		iconPosition: "beginning",
		label: null,
		showLabel: true
	},

	_getCreateOptions: function() {
		var disabled,

			// This is to support cases like in jQuery Mobile where the base widget does have
			// an implementation of _getCreateOptions
			options = this._super() || {};

		this.isInput = this.element.is( "input" );

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}

		this.originalLabel = this.isInput ? this.element.val() : this.element.html();
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		return options;
	},

	_create: function() {
		if ( !this.option.showLabel & !this.options.icon ) {
			this.options.showLabel = true;
		}

		// We have to check the option again here even though we did in _getCreateOptions,
		// because null may have been passed on init which would override what was set in
		// _getCreateOptions
		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled || false;
		}

		this.hasTitle = !!this.element.attr( "title" );

		// Check to see if the label needs to be set or if its already correct
		if ( this.options.label && this.options.label !== this.originalLabel ) {
			if ( this.isInput ) {
				this.element.val( this.options.label );
			} else {
				this.element.html( this.options.label );
			}
		}
		this._addClass( "ui-button", "ui-widget" );
		this._setOption( "disabled", this.options.disabled );
		this._enhance();

		if ( this.element.is( "a" ) ) {
			this._on( {
				"keyup": function( event ) {
					if ( event.keyCode === $.ui.keyCode.SPACE ) {
						event.preventDefault();

						// Support: PhantomJS <= 1.9, IE 8 Only
						// If a native click is available use it so we actually cause navigation
						// otherwise just trigger a click event
						if ( this.element[ 0 ].click ) {
							this.element[ 0 ].click();
						} else {
							this.element.trigger( "click" );
						}
					}
				}
			} );
		}
	},

	_enhance: function() {
		if ( !this.element.is( "button" ) ) {
			this.element.attr( "role", "button" );
		}

		if ( this.options.icon ) {
			this._updateIcon( "icon", this.options.icon );
			this._updateTooltip();
		}
	},

	_updateTooltip: function() {
		this.title = this.element.attr( "title" );

		if ( !this.options.showLabel && !this.title ) {
			this.element.attr( "title", this.options.label );
		}
	},

	_updateIcon: function( option, value ) {
		var icon = option !== "iconPosition",
			position = icon ? this.options.iconPosition : value,
			displayBlock = position === "top" || position === "bottom";

		// Create icon
		if ( !this.icon ) {
			this.icon = $( "<span>" );

			this._addClass( this.icon, "ui-button-icon", "ui-icon" );

			if ( !this.options.showLabel ) {
				this._addClass( "ui-button-icon-only" );
			}
		} else if ( icon ) {

			// If we are updating the icon remove the old icon class
			this._removeClass( this.icon, null, this.options.icon );
		}

		// If we are updating the icon add the new icon class
		if ( icon ) {
			this._addClass( this.icon, null, value );
		}

		this._attachIcon( position );

		// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
		// the iconSpace if there is one.
		if ( displayBlock ) {
			this._addClass( this.icon, null, "ui-widget-icon-block" );
			if ( this.iconSpace ) {
				this.iconSpace.remove();
			}
		} else {

			// Position is beginning or end so remove the ui-widget-icon-block class and add the
			// space if it does not exist
			if ( !this.iconSpace ) {
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-button-icon-space" );
			}
			this._removeClass( this.icon, null, "ui-wiget-icon-block" );
			this._attachIconSpace( position );
		}
	},

	_destroy: function() {
		this.element.removeAttr( "role" );

		if ( this.icon ) {
			this.icon.remove();
		}
		if ( this.iconSpace ) {
			this.iconSpace.remove();
		}
		if ( !this.hasTitle ) {
			this.element.removeAttr( "title" );
		}
	},

	_attachIconSpace: function( iconPosition ) {
		this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
	},

	_attachIcon: function( iconPosition ) {
		this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
	},

	_setOptions: function( options ) {
		var newShowLabel = options.showLabel === undefined ?
				this.options.showLabel :
				options.showLabel,
			newIcon = options.icon === undefined ? this.options.icon : options.icon;

		if ( !newShowLabel && !newIcon ) {
			options.showLabel = true;
		}
		this._super( options );
	},

	_setOption: function( key, value ) {
		if ( key === "icon" ) {
			if ( value ) {
				this._updateIcon( key, value );
			} else if ( this.icon ) {
				this.icon.remove();
				if ( this.iconSpace ) {
					this.iconSpace.remove();
				}
			}
		}

		if ( key === "iconPosition" ) {
			this._updateIcon( key, value );
		}

		// Make sure we can't end up with a button that has neither text nor icon
		if ( key === "showLabel" ) {
				this._toggleClass( this._classes( "ui-button-icon-only" ), null, !value );
				this._updateTooltip();
		}

		if ( key === "label" ) {
			if ( this.isInput ) {
				this.element.val( value );
			} else {

				// If there is an icon, append it, else nothing then append the value
				// this avoids removal of the icon when setting label text
				this.element.html( value );
				if ( this.icon ) {
					this._attachIcon( this.options.iconPosition );
					this._attachIconSpace( this.options.iconPosition );
				}
			}
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;
			if ( value ) {
				this.element.blur();
			}
		}
	},

	refresh: function() {

		// Make sure to only check disabled if its an element that supports this otherwise
		// check for the disabled class to determine state
		var isDisabled = this.element.is( "input, button" ) ?
			this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { disabled: isDisabled } );
		}

		this._updateTooltip();
	}
} );

// DEPRECATED
if ( $.uiBackCompat !== false ) {

	// Text and Icons options
	$.widget( "ui.button", $.ui.button, {
		options: {
			text: true,
			icons: {
				primary: null,
				secondary: null
			}
		},

		_create: function() {
			if ( this.options.showLabel && !this.options.text ) {
				this.options.showLabel = this.options.text;
			}
			if ( !this.options.showLabel && this.options.text ) {
				this.options.text = this.options.showLabel;
			}
			if ( !this.options.icon && ( this.options.icons.primary ||
					this.options.icons.secondary ) ) {
				if ( this.options.icons.primary ) {
					this.options.icon = this.options.icons.primary;
				} else {
					this.options.icon = this.options.icons.secondary;
					this.options.iconPosition = "end";
				}
			} else if ( this.options.icon ) {
				this.options.icons.primary = this.options.icon;
			}
			this._super();
		},

		_setOption: function( key, value ) {
			if ( key === "text" ) {
				this._super( "showLabel", value );
				return;
			}
			if ( key === "showLabel" ) {
				this.options.text = value;
			}
			if ( key === "icon" ) {
				this.options.icons.primary = value;
			}
			if ( key === "icons" ) {
				if ( value.primary ) {
					this._super( "icon", value.primary );
					this._super( "iconPosition", "beginning" );
				} else if ( value.secondary ) {
					this._super( "icon", value.secondary );
					this._super( "iconPosition", "end" );
				}
			}
			this._superApply( arguments );
		}
	} );

	$.fn.button = ( function( orig ) {
		return function() {
			if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
					( this.length && this[ 0 ].tagName === "INPUT" && (
						this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
					) ) ) {
				return orig.apply( this, arguments );
			}
			if ( !$.ui.checkboxradio ) {
				$.error( "Checkboxradio widget missing" );
			}
			if ( arguments.length === 0 ) {
				return this.checkboxradio( {
					"icon": false
				} );
			}
			return this.checkboxradio.apply( this, arguments );
		};
	} )( $.fn.button );

	$.fn.buttonset = function() {
		if ( !$.ui.controlgroup ) {
			$.error( "Controlgroup widget missing" );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
			return this.controlgroup.apply( this,
				[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
			return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
		}
		if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
			arguments[ 0 ].items = {
				button: arguments[ 0 ].items
			};
		}
		return this.controlgroup.apply( this, arguments );
	};
}

return $.ui.button;

} ) );
<|endoftext|>


<commit_before>import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import {ROOT, SRC_DIR, BUNDLE_DIR} from './constants';

export default function makeWebpackConfig(isDev) {
    return {
        context: ROOT,
        entry: {
            bundle: `${SRC_DIR}/index.js`
        },
        debug: isDev,
        devtool: isDev ? 'cheap-module-source-map' : '',
        watch: isDev,
        output: {
            path: BUNDLE_DIR,
            filename: '[name].js',
            sourceMapFilename: '[file].[hash].map',
            publicPath: '/build/'
        },
        module: {
            loaders: [{
                exclude: [/node_modules/],
                test: /\.js$/,
                loaders: ['babel', `js-assert/webpack-assert-loader?dev=${isDev ? 'true' : 'false'}`]
            }, {
                exclude: [/node_modules/],
                test: /\.css$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
            }, {
                exclude: [/node_modules/],
                test: /\.s(c|a)ss$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader')
            }]
        },
        plugins: [
            // Define a "__DEV__" variable to add code only for debug mode
            // e.g. __DEV__ && someDebugOnlyCheck();
            new webpack.DefinePlugin({
                '__DEV__': isDev
            }),
            new ExtractTextPlugin('[name].css')
        ].concat(isDev ? [] : [
            new webpack.optimize.DedupePlugin(),
            new webpack.optimize.OccurenceOrderPlugin(),
            new webpack.optimize.UglifyJsPlugin({
                compress: {
                    warnings: false,
                    screw_ie8: true
                }
            })
        ])
    };
}
<commit_msg>webpack config: adjust to default writting into the same source map file<commit_after>import webpack from 'webpack';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import {ROOT, SRC_DIR, BUNDLE_DIR} from './constants';

export default function makeWebpackConfig(isDev) {
    return {
        context: ROOT,
        entry: {
            bundle: `${SRC_DIR}/index.js`
        },
        debug: isDev,
        devtool: isDev ? 'cheap-module-source-map' : '',
        watch: isDev,
        output: {
            path: BUNDLE_DIR,
            filename: '[name].js',
            //sourceMapFilename: '[file].[hash].map', // include this config if meeting problem that source map is cached on browser
            publicPath: '/build/'
        },
        module: {
            loaders: [{
                exclude: [/node_modules/],
                test: /\.js$/,
                loaders: ['babel', `js-assert/webpack-assert-loader?dev=${isDev ? 'true' : 'false'}`]
            }, {
                exclude: [/node_modules/],
                test: /\.css$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
            }, {
                exclude: [/node_modules/],
                test: /\.s(c|a)ss$/,
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader')
            }]
        },
        plugins: [
            // Define a "__DEV__" variable to add code only for debug mode
            // e.g. __DEV__ && someDebugOnlyCheck();
            new webpack.DefinePlugin({
                '__DEV__': isDev
            }),
            new ExtractTextPlugin('[name].css')
        ].concat(isDev ? [] : [
            new webpack.optimize.DedupePlugin(),
            new webpack.optimize.OccurenceOrderPlugin(),
            new webpack.optimize.UglifyJsPlugin({
                compress: {
                    warnings: false,
                    screw_ie8: true
                }
            })
        ])
    };
}
<|endoftext|>


<commit_before>const Plugin = require('../Plugin')
const Client = require('./Client')
const StatusSocket = require('./Socket')

/**
 * Upload files to Transloadit using Tus.
 */
module.exports = class Transloadit extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Transloadit'
    this.title = 'Transloadit'

    const defaultLocale = {
      strings: {
        creatingAssembly: 'Preparing upload...',
        creatingAssemblyFailed: 'Transloadit: Could not create assembly',
        encoding: 'Encoding...'
      }
    }

    const defaultOptions = {
      waitForEncoding: false,
      waitForMetadata: false,
      signature: null,
      params: null,
      fields: {},
      getAssemblyOptions (file, options) {
        return {
          params: options.params,
          signature: options.signature,
          fields: options.fields
        }
      },
      locale: defaultLocale
    }

    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    this.prepareUpload = this.prepareUpload.bind(this)
    this.afterUpload = this.afterUpload.bind(this)

    if (this.opts.params) {
      this.validateParams(this.opts.params)
    }

    this.client = new Client()
    this.sockets = {}
  }

  validateParams (params) {
    if (!params) {
      throw new Error('Transloadit: The `params` option is required.')
    }

    if (typeof params === 'string') {
      try {
        params = JSON.parse(params)
      } catch (err) {
        // Tell the user that this is not an Uppy bug!
        err.message = 'Transloadit: The `params` option is a malformed JSON string: ' +
          err.message
        throw err
      }
    }

    if (!params.auth || !params.auth.key) {
      throw new Error('Transloadit: The `params.auth.key` option is required. ' +
        'You can find your Transloadit API key at https://transloadit.com/accounts/credentials.')
    }
  }

  getAssemblyOptions (fileIDs) {
    const options = this.opts
    return Promise.all(
      fileIDs.map((fileID) => {
        const file = this.core.getFile(fileID)
        const promise = Promise.resolve(options.getAssemblyOptions(file, options))
        return promise.then((assemblyOptions) => {
          this.validateParams(assemblyOptions.params)

          return {
            fileIDs: [fileID],
            options: assemblyOptions
          }
        })
      })
    )
  }

  dedupeAssemblyOptions (list) {
    const dedupeMap = Object.create(null)
    list.forEach(({ fileIDs, options }) => {
      const id = JSON.stringify(options)
      if (dedupeMap[id]) {
        dedupeMap[id].fileIDs.push(...fileIDs)
      } else {
        dedupeMap[id] = {
          options,
          fileIDs: [...fileIDs]
        }
      }
    })

    return Object.keys(dedupeMap).map((id) => dedupeMap[id])
  }

  createAssembly (fileIDs, options) {
    this.core.log('Transloadit: create assembly')

    return this.client.createAssembly({
      params: options.params,
      fields: options.fields,
      expectedFiles: fileIDs.length,
      signature: options.signature
    }).then((assembly) => {
      this.updateState({
        assemblies: Object.assign(this.state.assemblies, {
          [assembly.assembly_id]: assembly
        })
      })

      function attachAssemblyMetadata (file, assembly) {
        // Attach meta parameters for the Tus plugin. See:
        // https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus
        // TODO Should this `meta` be moved to a `tus.meta` property instead?
        // If the MetaData plugin can add eg. resize parameters, it doesn't
        // make much sense to set those as upload-metadata for tus.
        const meta = Object.assign({}, file.meta, {
          assembly_url: assembly.assembly_url,
          filename: file.name,
          fieldname: 'file'
        })
        // Add assembly-specific Tus endpoint.
        const tus = Object.assign({}, file.tus, {
          endpoint: assembly.tus_url
        })
        const transloadit = {
          assembly: assembly.assembly_id
        }
        return Object.assign(
          {},
          file,
          { meta, tus, transloadit }
        )
      }

      const files = Object.assign({}, this.core.state.files)
      fileIDs.forEach((id) => {
        files[id] = attachAssemblyMetadata(files[id], assembly)
      })

      this.core.setState({ files })

      this.core.emit('transloadit:assembly-created', assembly, fileIDs)

      return this.connectSocket(assembly)
    }).then(() => {
      this.core.log('Transloadit: Created assembly')
    }).catch((err) => {
      this.core.emit('informer', this.opts.locale.strings.creatingAssemblyFailed, 'error', 0)

      // Reject the promise.
      throw err
    })
  }

  shouldWait () {
    return this.opts.waitForEncoding || this.opts.waitForMetadata
  }

  findFile (uploadedFile) {
    const files = this.core.state.files
    for (const id in files) {
      if (!files.hasOwnProperty(id)) {
        continue
      }
      if (files[id].uploadURL === uploadedFile.tus_upload_url) {
        return files[id]
      }
    }
  }

  onFileUploadComplete (assemblyId, uploadedFile) {
    const file = this.findFile(uploadedFile)
    this.updateState({
      files: Object.assign({}, this.state.files, {
        [uploadedFile.id]: {
          id: file.id,
          uploadedFile
        }
      })
    })
    this.core.bus.emit('transloadit:upload', uploadedFile, this.getAssembly(assemblyId))
  }

  onResult (assemblyId, stepName, result) {
    const file = this.state.files[result.original_id]
    // The `file` may not exist if an import robot was used instead of a file upload.
    result.localId = file ? file.id : null

    this.updateState({
      results: this.state.results.concat(result)
    })
    this.core.bus.emit('transloadit:result', stepName, result, this.getAssembly(assemblyId))
  }

  onAssemblyFinished (url) {
    this.client.getAssemblyStatus(url).then((assembly) => {
      this.updateState({
        assemblies: Object.assign({}, this.state.assemblies, {
          [assembly.assembly_id]: assembly
        })
      })
      this.core.emit('transloadit:complete', assembly)
    })
  }

  connectSocket (assembly) {
    const socket = new StatusSocket(
      assembly.websocket_url,
      assembly
    )
    this.sockets[assembly.assembly_id] = socket

    socket.on('upload', this.onFileUploadComplete.bind(this, assembly.assembly_id))
    socket.on('error', (error) => {
      this.core.emit('transloadit:assembly-error', assembly, error)
    })

    if (this.opts.waitForEncoding) {
      socket.on('result', this.onResult.bind(this, assembly.assembly_id))
    }

    if (this.opts.waitForEncoding) {
      socket.on('finished', () => {
        this.onAssemblyFinished(assembly.assembly_ssl_url)
      })
    } else if (this.opts.waitForMetadata) {
      socket.on('metadata', () => {
        this.onAssemblyFinished(assembly.assembly_ssl_url)
        this.core.emit('transloadit:complete', assembly)
      })
    }

    return new Promise((resolve, reject) => {
      socket.on('connect', resolve)
      socket.on('error', reject)
    }).then(() => {
      this.core.log('Transloadit: Socket is ready')
    })
  }

  prepareUpload (fileIDs) {
    fileIDs.forEach((fileID) => {
      this.core.emit('core:preprocess-progress', fileID, {
        mode: 'indeterminate',
        message: this.opts.locale.strings.creatingAssembly
      })
    })

    const createAssembly = ({ fileIDs, options }) => {
      return this.createAssembly(fileIDs, options).then(() => {
        fileIDs.forEach((fileID) => {
          this.core.emit('core:preprocess-complete', fileID)
        })
      })
    }

    return this.getAssemblyOptions(fileIDs)
      .then((allOptions) => this.dedupeAssemblyOptions(allOptions))
      .then((assemblies) => Promise.all(
        assemblies.map(createAssembly)
      ))
  }

  afterUpload (fileIDs) {
    // A file ID that is part of this assembly...
    const fileID = fileIDs[0]

    // If we don't have to wait for encoding metadata or results, we can close
    // the socket immediately and finish the upload.
    if (!this.shouldWait()) {
      const file = this.core.getFile(fileID)
      const socket = this.socket[file.assembly]
      socket.close()
      return
    }

    return new Promise((resolve, reject) => {
      fileIDs.forEach((fileID) => {
        this.core.emit('core:postprocess-progress', fileID, {
          mode: 'indeterminate',
          message: this.opts.locale.strings.encoding
        })
      })

      const onAssemblyFinished = (assembly) => {
        const file = this.core.getFile(fileID)
        // An assembly for a different upload just finished. We can ignore it.
        if (assembly.assembly_id !== file.transloadit.assembly) {
          return
        }
        // Remove this handler once we find the assembly we needed.
        this.core.emitter.off('transloadit:complete', onAssemblyFinished)

        // TODO set the `file.uploadURL` to a result?
        // We will probably need an option here so the plugin user can tell us
        // which result to pick…?

        fileIDs.forEach((fileID) => {
          this.core.emit('core:postprocess-complete', fileID)
        })

        resolve()
      }

      const onAssemblyError = (assembly, error) => {
        const file = this.core.getFile(fileID)
        // An assembly for a different upload just errored. We can ignore it.
        if (assembly.assembly_id !== file.transloadit.assembly) {
          return
        }
        // Remove this handler once we find the assembly we needed.
        this.core.emitter.off('transloadit:assembly-error', onAssemblyError)

        // Clear postprocessing state for all our files.
        fileIDs.forEach((fileID) => {
          this.core.emit('core:postprocess-complete', fileID)
        })

        // Reject the `afterUpload()` promise.
        reject(error)
      }

      this.core.on('transloadit:complete', onAssemblyFinished)
      this.core.on('transloadit:assembly-error', onAssemblyError)
    })
  }

  install () {
    this.core.addPreProcessor(this.prepareUpload)
    this.core.addPostProcessor(this.afterUpload)

    this.updateState({
      assemblies: {},
      files: {},
      results: []
    })
  }

  uninstall () {
    this.core.removePreProcessor(this.prepareUpload)
    this.core.removePostProcessor(this.afterUpload)
  }

  getAssembly (id) {
    return this.state.assemblies[id]
  }

  get state () {
    return this.core.state.transloadit || {}
  }

  updateState (newState) {
    const transloadit = Object.assign({}, this.state, newState)

    this.core.setState({ transloadit })
  }
}
<commit_msg>transloadit: Fix crash when `waitFor` options are false<commit_after>const Plugin = require('../Plugin')
const Client = require('./Client')
const StatusSocket = require('./Socket')

/**
 * Upload files to Transloadit using Tus.
 */
module.exports = class Transloadit extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.type = 'uploader'
    this.id = 'Transloadit'
    this.title = 'Transloadit'

    const defaultLocale = {
      strings: {
        creatingAssembly: 'Preparing upload...',
        creatingAssemblyFailed: 'Transloadit: Could not create assembly',
        encoding: 'Encoding...'
      }
    }

    const defaultOptions = {
      waitForEncoding: false,
      waitForMetadata: false,
      signature: null,
      params: null,
      fields: {},
      getAssemblyOptions (file, options) {
        return {
          params: options.params,
          signature: options.signature,
          fields: options.fields
        }
      },
      locale: defaultLocale
    }

    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    this.prepareUpload = this.prepareUpload.bind(this)
    this.afterUpload = this.afterUpload.bind(this)

    if (this.opts.params) {
      this.validateParams(this.opts.params)
    }

    this.client = new Client()
    this.sockets = {}
  }

  validateParams (params) {
    if (!params) {
      throw new Error('Transloadit: The `params` option is required.')
    }

    if (typeof params === 'string') {
      try {
        params = JSON.parse(params)
      } catch (err) {
        // Tell the user that this is not an Uppy bug!
        err.message = 'Transloadit: The `params` option is a malformed JSON string: ' +
          err.message
        throw err
      }
    }

    if (!params.auth || !params.auth.key) {
      throw new Error('Transloadit: The `params.auth.key` option is required. ' +
        'You can find your Transloadit API key at https://transloadit.com/accounts/credentials.')
    }
  }

  getAssemblyOptions (fileIDs) {
    const options = this.opts
    return Promise.all(
      fileIDs.map((fileID) => {
        const file = this.core.getFile(fileID)
        const promise = Promise.resolve(options.getAssemblyOptions(file, options))
        return promise.then((assemblyOptions) => {
          this.validateParams(assemblyOptions.params)

          return {
            fileIDs: [fileID],
            options: assemblyOptions
          }
        })
      })
    )
  }

  dedupeAssemblyOptions (list) {
    const dedupeMap = Object.create(null)
    list.forEach(({ fileIDs, options }) => {
      const id = JSON.stringify(options)
      if (dedupeMap[id]) {
        dedupeMap[id].fileIDs.push(...fileIDs)
      } else {
        dedupeMap[id] = {
          options,
          fileIDs: [...fileIDs]
        }
      }
    })

    return Object.keys(dedupeMap).map((id) => dedupeMap[id])
  }

  createAssembly (fileIDs, options) {
    this.core.log('Transloadit: create assembly')

    return this.client.createAssembly({
      params: options.params,
      fields: options.fields,
      expectedFiles: fileIDs.length,
      signature: options.signature
    }).then((assembly) => {
      this.updateState({
        assemblies: Object.assign(this.state.assemblies, {
          [assembly.assembly_id]: assembly
        })
      })

      function attachAssemblyMetadata (file, assembly) {
        // Attach meta parameters for the Tus plugin. See:
        // https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus
        // TODO Should this `meta` be moved to a `tus.meta` property instead?
        // If the MetaData plugin can add eg. resize parameters, it doesn't
        // make much sense to set those as upload-metadata for tus.
        const meta = Object.assign({}, file.meta, {
          assembly_url: assembly.assembly_url,
          filename: file.name,
          fieldname: 'file'
        })
        // Add assembly-specific Tus endpoint.
        const tus = Object.assign({}, file.tus, {
          endpoint: assembly.tus_url
        })
        const transloadit = {
          assembly: assembly.assembly_id
        }
        return Object.assign(
          {},
          file,
          { meta, tus, transloadit }
        )
      }

      const files = Object.assign({}, this.core.state.files)
      fileIDs.forEach((id) => {
        files[id] = attachAssemblyMetadata(files[id], assembly)
      })

      this.core.setState({ files })

      this.core.emit('transloadit:assembly-created', assembly, fileIDs)

      return this.connectSocket(assembly)
    }).then(() => {
      this.core.log('Transloadit: Created assembly')
    }).catch((err) => {
      this.core.emit('informer', this.opts.locale.strings.creatingAssemblyFailed, 'error', 0)

      // Reject the promise.
      throw err
    })
  }

  shouldWait () {
    return this.opts.waitForEncoding || this.opts.waitForMetadata
  }

  findFile (uploadedFile) {
    const files = this.core.state.files
    for (const id in files) {
      if (!files.hasOwnProperty(id)) {
        continue
      }
      if (files[id].uploadURL === uploadedFile.tus_upload_url) {
        return files[id]
      }
    }
  }

  onFileUploadComplete (assemblyId, uploadedFile) {
    const file = this.findFile(uploadedFile)
    this.updateState({
      files: Object.assign({}, this.state.files, {
        [uploadedFile.id]: {
          id: file.id,
          uploadedFile
        }
      })
    })
    this.core.bus.emit('transloadit:upload', uploadedFile, this.getAssembly(assemblyId))
  }

  onResult (assemblyId, stepName, result) {
    const file = this.state.files[result.original_id]
    // The `file` may not exist if an import robot was used instead of a file upload.
    result.localId = file ? file.id : null

    this.updateState({
      results: this.state.results.concat(result)
    })
    this.core.bus.emit('transloadit:result', stepName, result, this.getAssembly(assemblyId))
  }

  onAssemblyFinished (url) {
    this.client.getAssemblyStatus(url).then((assembly) => {
      this.updateState({
        assemblies: Object.assign({}, this.state.assemblies, {
          [assembly.assembly_id]: assembly
        })
      })
      this.core.emit('transloadit:complete', assembly)
    })
  }

  connectSocket (assembly) {
    const socket = new StatusSocket(
      assembly.websocket_url,
      assembly
    )
    this.sockets[assembly.assembly_id] = socket

    socket.on('upload', this.onFileUploadComplete.bind(this, assembly.assembly_id))
    socket.on('error', (error) => {
      this.core.emit('transloadit:assembly-error', assembly, error)
    })

    if (this.opts.waitForEncoding) {
      socket.on('result', this.onResult.bind(this, assembly.assembly_id))
    }

    if (this.opts.waitForEncoding) {
      socket.on('finished', () => {
        this.onAssemblyFinished(assembly.assembly_ssl_url)
      })
    } else if (this.opts.waitForMetadata) {
      socket.on('metadata', () => {
        this.onAssemblyFinished(assembly.assembly_ssl_url)
        this.core.emit('transloadit:complete', assembly)
      })
    }

    return new Promise((resolve, reject) => {
      socket.on('connect', resolve)
      socket.on('error', reject)
    }).then(() => {
      this.core.log('Transloadit: Socket is ready')
    })
  }

  prepareUpload (fileIDs) {
    fileIDs.forEach((fileID) => {
      this.core.emit('core:preprocess-progress', fileID, {
        mode: 'indeterminate',
        message: this.opts.locale.strings.creatingAssembly
      })
    })

    const createAssembly = ({ fileIDs, options }) => {
      return this.createAssembly(fileIDs, options).then(() => {
        fileIDs.forEach((fileID) => {
          this.core.emit('core:preprocess-complete', fileID)
        })
      })
    }

    return this.getAssemblyOptions(fileIDs)
      .then((allOptions) => this.dedupeAssemblyOptions(allOptions))
      .then((assemblies) => Promise.all(
        assemblies.map(createAssembly)
      ))
  }

  afterUpload (fileIDs) {
    // A file ID that is part of this assembly...
    const fileID = fileIDs[0]

    // If we don't have to wait for encoding metadata or results, we can close
    // the socket immediately and finish the upload.
    if (!this.shouldWait()) {
      const file = this.core.getFile(fileID)
      const socket = this.sockets[file.transloadit.assembly]
      socket.close()
      return Promise.resolve()
    }

    return new Promise((resolve, reject) => {
      fileIDs.forEach((fileID) => {
        this.core.emit('core:postprocess-progress', fileID, {
          mode: 'indeterminate',
          message: this.opts.locale.strings.encoding
        })
      })

      const onAssemblyFinished = (assembly) => {
        const file = this.core.getFile(fileID)
        // An assembly for a different upload just finished. We can ignore it.
        if (assembly.assembly_id !== file.transloadit.assembly) {
          return
        }
        // Remove this handler once we find the assembly we needed.
        this.core.emitter.off('transloadit:complete', onAssemblyFinished)

        // TODO set the `file.uploadURL` to a result?
        // We will probably need an option here so the plugin user can tell us
        // which result to pick…?

        fileIDs.forEach((fileID) => {
          this.core.emit('core:postprocess-complete', fileID)
        })

        resolve()
      }

      const onAssemblyError = (assembly, error) => {
        const file = this.core.getFile(fileID)
        // An assembly for a different upload just errored. We can ignore it.
        if (assembly.assembly_id !== file.transloadit.assembly) {
          return
        }
        // Remove this handler once we find the assembly we needed.
        this.core.emitter.off('transloadit:assembly-error', onAssemblyError)

        // Clear postprocessing state for all our files.
        fileIDs.forEach((fileID) => {
          this.core.emit('core:postprocess-complete', fileID)
        })

        // Reject the `afterUpload()` promise.
        reject(error)
      }

      this.core.on('transloadit:complete', onAssemblyFinished)
      this.core.on('transloadit:assembly-error', onAssemblyError)
    })
  }

  install () {
    this.core.addPreProcessor(this.prepareUpload)
    this.core.addPostProcessor(this.afterUpload)

    this.updateState({
      assemblies: {},
      files: {},
      results: []
    })
  }

  uninstall () {
    this.core.removePreProcessor(this.prepareUpload)
    this.core.removePostProcessor(this.afterUpload)
  }

  getAssembly (id) {
    return this.state.assemblies[id]
  }

  get state () {
    return this.core.state.transloadit || {}
  }

  updateState (newState) {
    const transloadit = Object.assign({}, this.state, newState)

    this.core.setState({ transloadit })
  }
}
<|endoftext|>


<commit_before>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true, loopfunc: true*/
/*global prepros, _*/

prepros.factory('javascript', function (config, utils) {

    'use strict';

    var fs = require('fs-extra'),
        path = require('path'),
        _id = utils.id;

    var format = function (pid, fid, filePath, projectPath) {

        //File name
        var name = path.basename(filePath);

        // Output path
        var output = path.join(path.dirname(filePath), '{{jsMinPath}}', path.basename(filePath).replace(/\.js/gi, '.min.js'));

        return {

            id: fid,
            pid: pid,
            name: name,
            type: 'JS',
            input: path.relative(projectPath, filePath),
            output: path.relative(projectPath, output),
            config: config.getUserOptions().javascript
        };
    };

    var compile = function (file, successCall, errorCall) {

        var ugly = require('uglify-js');

        var options = {};

        fs.readFile(file.input, { encoding: 'utf8' }, function (err, data) {

            if (err) {

                errorCall(err.message);

            } else {

                var run = function () {

                    var javascript = data.toString();

                    if (file.config.uglify) {

                        try {

                            javascript = ugly.minify(javascript, {fromString: true, mangle: file.config.mangle}).code;

                        } catch (e) {

                            throw {message: 'Error on line ' + e.line + ' col ' + e.col + ' ' + e.message + ' of ' + file.input};
                        }
                    }

                    var importReg = {
                        append: /\/\/(?:\s|)@(?:prepros|codekit)-append\s+(.*)/gi,
                        prepend: /\/\/(?:\s|)@(?:prepros|codekit)-prepend\s+(.*)/gi
                    };

                    var read = function (filePathToRead) {

                        var importedFiles = {
                            append: [],
                            prepend: []
                        };

                        var regs = Object.keys(importReg);

                        _.each(regs, function (reg) {

                            var result;

                            while ((result = importReg[reg].exec(fs.readFileSync(filePathToRead))) !== null) {

                                var importedFile;

                                result[1] = result[1].replace(/'|"/gi, '').trim();

                                //Check if path is full or just relative
                                if (result[1].indexOf(':') >= 0) {

                                    importedFile = path.normalize(result[1]);

                                } else {

                                    importedFile = path.join(path.dirname(filePathToRead), result[1]);
                                }

                                //Check if file exists
                                if (fs.existsSync(importedFile)) {

                                    importedFiles[reg].push(importedFile);

                                } else {

                                    throw {message: 'Imported file "' + importedFile + '" not found \n Imported by "' + file.input + '"'};
                                }
                            }
                        });

                        return {
                            append: importedFiles.append,
                            prepend: importedFiles.prepend.reverse()
                        };
                    };

                    var get = function (append) {

                        var imps = [];
                        imps[0] = (append) ? read(file.input).append : read(file.input).prepend;

                        //Get imports of imports up to four levels
                        for (var i = 1; i < 5; i++) {

                            imps[i] = [];

                            _.each(imps[i - 1], function (importedFile) {

                                imps[i] = _.uniq(_.union(imps[i], (append) ? read(importedFile).append : read(importedFile).prepend));
                            });
                        }

                        return _.uniq(_.flatten(imps));

                    };

                    var join = function (files, append) {

                        //Remove repeated imports
                        _.each(_.uniq(_.flatten(files)), function (imp) {

                            var js = fs.readFileSync(imp).toString();

                            if (file.config.uglify && !/min.js$/.exec(path.basename(imp))) {

                                try {

                                    js = ugly.minify(js, {fromString: true, mangle: file.config.mangle}).code;

                                } catch (e) {

                                    throw {message: 'Error on line ' + e.line + ' col ' + e.col + ' ' + e.message + ' of ' + imp};
                                }
                            }

                            if (append) {

                                javascript = javascript + js;

                            } else {

                                javascript = js + javascript;

                            }
                        });
                    };

                    //Join Files
                    var appends = get(true);
                    var prepends = get(false);
                    join(appends, true);
                    join(prepends, false);

                    _.each(importReg, function (reg) {

                        javascript = javascript.replace(new RegExp(reg.source + '\n', 'gi'), '');
                    });

                    try {
                        fs.outputFileSync(file.output, javascript);
                    } catch (e) {
                        throw e;
                    }
                };

                //try to compile js
                try {

                    run();

                    successCall(file.input);

                } catch (e) {
                    errorCall(e.message);
                }
            }
        });
    };

    return {
        format: format,
        compile: compile
    };
});<commit_msg>new line<commit_after>/**
 * Prepros
 * (c) Subash Pathak
 * sbshpthk@gmail.com
 * License: MIT
 */

/*jshint browser: true, node: true, loopfunc: true*/
/*global prepros, _*/

prepros.factory('javascript', function (config, utils) {

    'use strict';

    var fs = require('fs-extra'),
        path = require('path'),
        _id = utils.id;

    var format = function (pid, fid, filePath, projectPath) {

        //File name
        var name = path.basename(filePath);

        // Output path
        var output = path.join(path.dirname(filePath), '{{jsMinPath}}', path.basename(filePath).replace(/\.js/gi, '.min.js'));

        return {

            id: fid,
            pid: pid,
            name: name,
            type: 'JS',
            input: path.relative(projectPath, filePath),
            output: path.relative(projectPath, output),
            config: config.getUserOptions().javascript
        };
    };

    var compile = function (file, successCall, errorCall) {

        var ugly = require('uglify-js');

        var options = {};

        fs.readFile(file.input, { encoding: 'utf8' }, function (err, data) {

            if (err) {

                errorCall(err.message);

            } else {

                var run = function () {

                    var javascript = data.toString();

                    if (file.config.uglify) {

                        try {

                            javascript = ugly.minify(javascript, {fromString: true, mangle: file.config.mangle}).code;

                        } catch (e) {

                            throw {message: 'Error on line ' + e.line + ' col ' + e.col + ' ' + e.message + ' of ' + file.input};
                        }
                    }

                    var importReg = {
                        append: /\/\/(?:\s|)@(?:prepros|codekit)-append\s+(.*)/gi,
                        prepend: /\/\/(?:\s|)@(?:prepros|codekit)-prepend\s+(.*)/gi
                    };

                    var read = function (filePathToRead) {

                        var importedFiles = {
                            append: [],
                            prepend: []
                        };

                        var regs = Object.keys(importReg);

                        _.each(regs, function (reg) {

                            var result;

                            while ((result = importReg[reg].exec(fs.readFileSync(filePathToRead))) !== null) {

                                var importedFile;

                                result[1] = result[1].replace(/'|"/gi, '').trim();

                                //Check if path is full or just relative
                                if (result[1].indexOf(':') >= 0) {

                                    importedFile = path.normalize(result[1]);

                                } else {

                                    importedFile = path.join(path.dirname(filePathToRead), result[1]);
                                }

                                //Check if file exists
                                if (fs.existsSync(importedFile)) {

                                    importedFiles[reg].push(importedFile);

                                } else {

                                    throw {message: 'Imported file "' + importedFile + '" not found \n Imported by "' + file.input + '"'};
                                }
                            }
                        });

                        return {
                            append: importedFiles.append,
                            prepend: importedFiles.prepend.reverse()
                        };
                    };

                    var get = function (append) {

                        var imps = [];
                        imps[0] = (append) ? read(file.input).append : read(file.input).prepend;

                        //Get imports of imports up to four levels
                        for (var i = 1; i < 5; i++) {

                            imps[i] = [];

                            _.each(imps[i - 1], function (importedFile) {

                                imps[i] = _.uniq(_.union(imps[i], (append) ? read(importedFile).append : read(importedFile).prepend));
                            });
                        }

                        return _.uniq(_.flatten(imps));

                    };

                    var join = function (files, append) {

                        //Remove repeated imports
                        _.each(_.uniq(_.flatten(files)), function (imp) {

                            var js = fs.readFileSync(imp).toString();

                            if (file.config.uglify && !/min.js$/.exec(path.basename(imp))) {

                                try {

                                    js = ugly.minify(js, {fromString: true, mangle: file.config.mangle}).code;

                                } catch (e) {

                                    throw {message: 'Error on line ' + e.line + ' col ' + e.col + ' ' + e.message + ' of ' + imp};
                                }
                            }

                            if (append) {

                                javascript = javascript + '\n' + js;

                            } else {

                                javascript = js + '\n' + javascript;

                            }
                        });
                    };

                    //Join Files
                    var appends = get(true);
                    var prepends = get(false);
                    join(appends, true);
                    join(prepends, false);

                    _.each(importReg, function (reg) {

                        javascript = javascript.replace(new RegExp(reg.source + '\n', 'gi'), '');
                    });

                    try {
                        fs.outputFileSync(file.output, javascript);
                    } catch (e) {
                        throw e;
                    }
                };

                //try to compile js
                try {

                    run();

                    successCall(file.input);

                } catch (e) {
                    errorCall(e.message);
                }
            }
        });
    };

    return {
        format: format,
        compile: compile
    };
});<|endoftext|>


<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

'use strict';

const configCommon = require('config');
const config = configCommon.get('services.CoAuthoring');
//process.env.NODE_ENV = config.get('server.mode');
const logger = require('./../../Common/sources/logger');
const co = require('co');
const license = require('./../../Common/sources/license');
const fs = require('fs');

const express = require('express');
const http = require('http');
const urlModule = require('url');
const path = require('path');
const bodyParser = require("body-parser");
const mime = require('mime');
const docsCoServer = require('./DocsCoServer');
const canvasService = require('./canvasservice');
const converterService = require('./converterservice');
const fileUploaderService = require('./fileuploaderservice');
const wopiClient = require('./wopiClient');
const constants = require('./../../Common/sources/constants');
const utils = require('./../../Common/sources/utils');
const commonDefines = require('./../../Common/sources/commondefines');
const configStorage = configCommon.get('storage');

const cfgHtmlTemplate = configCommon.get('wopi.htmlTemplate');

const app = express();
app.set("views", cfgHtmlTemplate);
app.set("view engine", "ejs");
const server = http.createServer(app);

let licenseInfo, licenseOriginal, updatePluginsTime, userPlugins, pluginsLoaded;

const updatePlugins = (eventType, filename) => {
	console.log('update Folder: %s ; %s', eventType, filename);
	if (updatePluginsTime && 1000 >= (new Date() - updatePluginsTime)) {
		return;
	}
	console.log('update Folder true: %s ; %s', eventType, filename);
	updatePluginsTime = new Date();
	pluginsLoaded = false;
};
const readLicense = function*() {
	[licenseInfo, licenseOriginal] = yield* license.readLicense();
};
const updateLicense = () => {
	return co(function*() {
		try {
			yield* readLicense();
			docsCoServer.setLicenseInfo(licenseInfo, licenseOriginal);
			console.log('End updateLicense');
		} catch (err) {
			logger.error('updateLicense error:\r\n%s', err.stack);
		}
	});
};

logger.warn('Express server starting...');

updateLicense();

if (config.has('server.static_content')) {
	const staticContent = config.get('server.static_content');
	for (let i in staticContent) {
		app.use(i, express.static(staticContent[i]['path'], staticContent[i]['options']));
	}
}

if (configStorage.has('fs.folderPath')) {
	const cfgBucketName = configStorage.get('bucketName');
	const cfgStorageFolderName = configStorage.get('storageFolderName');
	app.use('/' + cfgBucketName + '/' + cfgStorageFolderName, (req, res, next) => {
		const index = req.url.lastIndexOf('/');
		if ('GET' === req.method && -1 != index) {
			const contentDisposition = req.query['disposition'] || 'attachment';
			let sendFileOptions = {
				root: configStorage.get('fs.folderPath'), dotfiles: 'deny', headers: {
					'Content-Disposition': contentDisposition
				}
			};
			const urlParsed = urlModule.parse(req.url);
			if (urlParsed && urlParsed.pathname) {
				const filename = decodeURIComponent(path.basename(urlParsed.pathname));
				sendFileOptions.headers['Content-Type'] = mime.getType(filename);
			}
			const realUrl = req.url.substring(0, index);
			res.sendFile(realUrl, sendFileOptions, (err) => {
				if (err) {
					logger.error(err);
					res.status(400).end();
				}
			});
		} else {
			res.sendStatus(404)
		}
	});
}

try {
	fs.watch(config.get('plugins.path'), updatePlugins);
} catch (e) {
	logger.warn('Failed to subscribe to plugin folder updates. When changing the list of plugins, you must restart the server. https://nodejs.org/docs/latest/api/fs.html#fs_availability');
}
fs.watchFile(configCommon.get('license').get('license_file'), updateLicense);
setInterval(updateLicense, 86400000);

// Если захочется использовать 'development' и 'production',
// то с помощью app.settings.env (https://github.com/strongloop/express/issues/936)
// Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler
docsCoServer.install(server, () => {
	console.log('Start callbackFunction');

	server.listen(config.get('server.port'), () => {
		logger.warn("Express server listening on port %d in %s mode. Version: %s. Build: %s", config.get('server.port'), app.settings.env, commonDefines.buildVersion, commonDefines.buildNumber);
	});

	app.get('/index.html', (req, res) => {
		res.send('Server is functioning normally. Version: ' + commonDefines.buildVersion + '. Build: ' +
			commonDefines.buildNumber);
	});
	const rawFileParser = bodyParser.raw(
		{inflate: true, limit: config.get('server.limits_tempfile_upload'), type: function() {return true;}});
	const urleEcodedParser = bodyParser.urlencoded({ extended: false });

	app.get('/coauthoring/CommandService.ashx', utils.checkClientIp, rawFileParser, docsCoServer.commandFromServer);
	app.post('/coauthoring/CommandService.ashx', utils.checkClientIp, rawFileParser,
		docsCoServer.commandFromServer);

	app.get('/ConvertService.ashx', utils.checkClientIp, rawFileParser, converterService.convertXml);
	app.post('/ConvertService.ashx', utils.checkClientIp, rawFileParser, converterService.convertXml);
	app.post('/converter', utils.checkClientIp, rawFileParser, converterService.convertJson);

	app.get('/FileUploader.ashx', utils.checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);
	app.post('/FileUploader.ashx', utils.checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);

	app.param('docid', (req, res, next, val) => {
		if (constants.DOC_ID_REGEX.test(val)) {
			next();
		} else {
			res.sendStatus(403);
		}
	});
	app.param('index', (req, res, next, val) => {
		if (!isNaN(parseInt(val))) {
			next();
		} else {
			res.sendStatus(403);
		}
	});
	app.post('/uploadold/:docid/:userid/:index', fileUploaderService.uploadImageFileOld);
	app.post('/upload/:docid/:userid/:index', rawFileParser, fileUploaderService.uploadImageFile);

	app.post('/downloadas/:docid', rawFileParser, canvasService.downloadAs);
	app.post('/savefile/:docid', rawFileParser, canvasService.saveFile);
	app.get('/healthcheck', utils.checkClientIp, docsCoServer.healthCheck);

	app.get('/baseurl', (req, res) => {
		res.send(utils.getBaseUrlByRequest(req));
	});

	app.get('/robots.txt', (req, res) => {
		res.setHeader('Content-Type', 'plain/text');
		res.send("User-agent: *\nDisallow: /");
	});

	app.post('/docbuilder', utils.checkClientIp, rawFileParser, (req, res) => {
		converterService.builder(req, res);
	});
	app.get('/info/info.json', utils.checkClientIp, docsCoServer.licenseInfo);
	app.put('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);
	app.delete('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);

	app.get('/hosting/discovery', utils.checkClientIp, wopiClient.discovery);
	app.post('/hosting/wopi', utils.checkClientIp, urleEcodedParser, utils.lowercaseQueryString, wopiClient.getEditorHtml);

	const sendUserPlugins = (res, data) => {
		pluginsLoaded = true;
		res.setHeader('Content-Type', 'application/json');
		res.send(JSON.stringify(data));
	};
	app.get('/plugins.json', (req, res) => {
		if (userPlugins && pluginsLoaded) {
			sendUserPlugins(res, userPlugins);
			return;
		}

		if (!config.has('server.static_content') || !config.has('plugins.uri')) {
			res.sendStatus(404);
			return;
		}

		let staticContent = config.get('server.static_content');
		let pluginsUri = config.get('plugins.uri');
		let pluginsPath = undefined;
		let pluginsAutostart = config.get('plugins.autostart');

		if (staticContent[pluginsUri]) {
			pluginsPath = staticContent[pluginsUri].path;
		}

		let baseUrl = '../../../..';
		utils.listFolders(pluginsPath, true).then((values) => {
			return co(function*() {
				const configFile = 'config.json';
				let stats = null;
				let result = [];
				for (let i = 0; i < values.length; ++i) {
					try {
						stats = yield utils.fsStat(path.join(values[i], configFile));
					} catch (err) {
						stats = null;
					}

					if (stats && stats.isFile) {
						result.push( baseUrl + pluginsUri + '/' + path.basename(values[i]) + '/' + configFile);
					}
				}

				userPlugins = {'url': '', 'pluginsData': result, 'autostart': pluginsAutostart};
				sendUserPlugins(res, userPlugins);
			});
		});
	});
});

process.on('uncaughtException', (err) => {
	logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
	logger.error(err.stack);
	logger.shutdown(() => {
		process.exit(1);
	});
});
<commit_msg>[feature] Add dummyCallback handler for simple callbackUrl<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

'use strict';

const configCommon = require('config');
const config = configCommon.get('services.CoAuthoring');
//process.env.NODE_ENV = config.get('server.mode');
const logger = require('./../../Common/sources/logger');
const co = require('co');
const license = require('./../../Common/sources/license');
const fs = require('fs');

const express = require('express');
const http = require('http');
const urlModule = require('url');
const path = require('path');
const bodyParser = require("body-parser");
const mime = require('mime');
const docsCoServer = require('./DocsCoServer');
const canvasService = require('./canvasservice');
const converterService = require('./converterservice');
const fileUploaderService = require('./fileuploaderservice');
const wopiClient = require('./wopiClient');
const constants = require('./../../Common/sources/constants');
const utils = require('./../../Common/sources/utils');
const commonDefines = require('./../../Common/sources/commondefines');
const configStorage = configCommon.get('storage');

const cfgHtmlTemplate = configCommon.get('wopi.htmlTemplate');

const app = express();
app.set("views", cfgHtmlTemplate);
app.set("view engine", "ejs");
const server = http.createServer(app);

let licenseInfo, licenseOriginal, updatePluginsTime, userPlugins, pluginsLoaded;

const updatePlugins = (eventType, filename) => {
	console.log('update Folder: %s ; %s', eventType, filename);
	if (updatePluginsTime && 1000 >= (new Date() - updatePluginsTime)) {
		return;
	}
	console.log('update Folder true: %s ; %s', eventType, filename);
	updatePluginsTime = new Date();
	pluginsLoaded = false;
};
const readLicense = function*() {
	[licenseInfo, licenseOriginal] = yield* license.readLicense();
};
const updateLicense = () => {
	return co(function*() {
		try {
			yield* readLicense();
			docsCoServer.setLicenseInfo(licenseInfo, licenseOriginal);
			console.log('End updateLicense');
		} catch (err) {
			logger.error('updateLicense error:\r\n%s', err.stack);
		}
	});
};

logger.warn('Express server starting...');

updateLicense();

if (config.has('server.static_content')) {
	const staticContent = config.get('server.static_content');
	for (let i in staticContent) {
		app.use(i, express.static(staticContent[i]['path'], staticContent[i]['options']));
	}
}

if (configStorage.has('fs.folderPath')) {
	const cfgBucketName = configStorage.get('bucketName');
	const cfgStorageFolderName = configStorage.get('storageFolderName');
	app.use('/' + cfgBucketName + '/' + cfgStorageFolderName, (req, res, next) => {
		const index = req.url.lastIndexOf('/');
		if ('GET' === req.method && -1 != index) {
			const contentDisposition = req.query['disposition'] || 'attachment';
			let sendFileOptions = {
				root: configStorage.get('fs.folderPath'), dotfiles: 'deny', headers: {
					'Content-Disposition': contentDisposition
				}
			};
			const urlParsed = urlModule.parse(req.url);
			if (urlParsed && urlParsed.pathname) {
				const filename = decodeURIComponent(path.basename(urlParsed.pathname));
				sendFileOptions.headers['Content-Type'] = mime.getType(filename);
			}
			const realUrl = req.url.substring(0, index);
			res.sendFile(realUrl, sendFileOptions, (err) => {
				if (err) {
					logger.error(err);
					res.status(400).end();
				}
			});
		} else {
			res.sendStatus(404)
		}
	});
}

try {
	fs.watch(config.get('plugins.path'), updatePlugins);
} catch (e) {
	logger.warn('Failed to subscribe to plugin folder updates. When changing the list of plugins, you must restart the server. https://nodejs.org/docs/latest/api/fs.html#fs_availability');
}
fs.watchFile(configCommon.get('license').get('license_file'), updateLicense);
setInterval(updateLicense, 86400000);

// Если захочется использовать 'development' и 'production',
// то с помощью app.settings.env (https://github.com/strongloop/express/issues/936)
// Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler
docsCoServer.install(server, () => {
	console.log('Start callbackFunction');

	server.listen(config.get('server.port'), () => {
		logger.warn("Express server listening on port %d in %s mode. Version: %s. Build: %s", config.get('server.port'), app.settings.env, commonDefines.buildVersion, commonDefines.buildNumber);
	});

	app.get('/index.html', (req, res) => {
		res.send('Server is functioning normally. Version: ' + commonDefines.buildVersion + '. Build: ' +
			commonDefines.buildNumber);
	});
	const rawFileParser = bodyParser.raw(
		{inflate: true, limit: config.get('server.limits_tempfile_upload'), type: function() {return true;}});
	const urleEcodedParser = bodyParser.urlencoded({ extended: false });

	app.get('/coauthoring/CommandService.ashx', utils.checkClientIp, rawFileParser, docsCoServer.commandFromServer);
	app.post('/coauthoring/CommandService.ashx', utils.checkClientIp, rawFileParser,
		docsCoServer.commandFromServer);

	app.get('/ConvertService.ashx', utils.checkClientIp, rawFileParser, converterService.convertXml);
	app.post('/ConvertService.ashx', utils.checkClientIp, rawFileParser, converterService.convertXml);
	app.post('/converter', utils.checkClientIp, rawFileParser, converterService.convertJson);

	app.get('/FileUploader.ashx', utils.checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);
	app.post('/FileUploader.ashx', utils.checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);

	app.param('docid', (req, res, next, val) => {
		if (constants.DOC_ID_REGEX.test(val)) {
			next();
		} else {
			res.sendStatus(403);
		}
	});
	app.param('index', (req, res, next, val) => {
		if (!isNaN(parseInt(val))) {
			next();
		} else {
			res.sendStatus(403);
		}
	});
	app.post('/uploadold/:docid/:userid/:index', fileUploaderService.uploadImageFileOld);
	app.post('/upload/:docid/:userid/:index', rawFileParser, fileUploaderService.uploadImageFile);

	app.post('/downloadas/:docid', rawFileParser, canvasService.downloadAs);
	app.post('/savefile/:docid', rawFileParser, canvasService.saveFile);
	app.get('/healthcheck', utils.checkClientIp, docsCoServer.healthCheck);

	app.get('/baseurl', (req, res) => {
		res.send(utils.getBaseUrlByRequest(req));
	});

	app.get('/robots.txt', (req, res) => {
		res.setHeader('Content-Type', 'plain/text');
		res.send("User-agent: *\nDisallow: /");
	});

	app.post('/docbuilder', utils.checkClientIp, rawFileParser, (req, res) => {
		converterService.builder(req, res);
	});
	app.get('/info/info.json', utils.checkClientIp, docsCoServer.licenseInfo);
	app.put('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);
	app.delete('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);

	app.get('/hosting/discovery', utils.checkClientIp, wopiClient.discovery);
	app.post('/hosting/wopi', utils.checkClientIp, urleEcodedParser, utils.lowercaseQueryString, wopiClient.getEditorHtml);

	app.post('/dummyCallback', utils.checkClientIp, rawFileParser, function(req, res){
		logger.debug(`dummyCallback req.body:%s`, req.body);
		utils.fillResponseSimple(res, JSON.stringify({error: 0}, "application/json"));
	});

	const sendUserPlugins = (res, data) => {
		pluginsLoaded = true;
		res.setHeader('Content-Type', 'application/json');
		res.send(JSON.stringify(data));
	};
	app.get('/plugins.json', (req, res) => {
		if (userPlugins && pluginsLoaded) {
			sendUserPlugins(res, userPlugins);
			return;
		}

		if (!config.has('server.static_content') || !config.has('plugins.uri')) {
			res.sendStatus(404);
			return;
		}

		let staticContent = config.get('server.static_content');
		let pluginsUri = config.get('plugins.uri');
		let pluginsPath = undefined;
		let pluginsAutostart = config.get('plugins.autostart');

		if (staticContent[pluginsUri]) {
			pluginsPath = staticContent[pluginsUri].path;
		}

		let baseUrl = '../../../..';
		utils.listFolders(pluginsPath, true).then((values) => {
			return co(function*() {
				const configFile = 'config.json';
				let stats = null;
				let result = [];
				for (let i = 0; i < values.length; ++i) {
					try {
						stats = yield utils.fsStat(path.join(values[i], configFile));
					} catch (err) {
						stats = null;
					}

					if (stats && stats.isFile) {
						result.push( baseUrl + pluginsUri + '/' + path.basename(values[i]) + '/' + configFile);
					}
				}

				userPlugins = {'url': '', 'pluginsData': result, 'autostart': pluginsAutostart};
				sendUserPlugins(res, userPlugins);
			});
		});
	});
});

process.on('uncaughtException', (err) => {
	logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
	logger.error(err.stack);
	logger.shutdown(() => {
		process.exit(1);
	});
});
<|endoftext|>


<commit_before>$(document).ready(function (){

	/* mobile nav button */
	$( 'nav .nav-btn' ).click( function(){
		var page_header_nav_ul = $( '#page-header nav ul' );

		page_header_nav_ul.toggleClass( 'active' );

		if( page_header_nav_ul.hasClass('active') ){
			$('html, body').animate({
				scrollTop: $("#page-header nav").offset().top
			}, 400);
			console.log('ok');
		}
	});

	/* Countdown */
	countDown( new Date("March 1, 2013 18:00:00") );
	setInterval( function() { countDown( new Date("March 1, 2013 18:00:00") ); }, 1000*60 );
	function countDown( date ){
		var today = new Date();
		msPerDay = 24 * 60 * 60 * 1000 ;
		timeLeft = (date.getTime() - today.getTime());
		var e_daysLeft = timeLeft / msPerDay;
		daysLeft = Math.floor(e_daysLeft);
		var e_hrsLeft = (e_daysLeft - daysLeft)*24;
		hrsLeft = Math.floor(e_hrsLeft);
		var minsLeft = Math.floor((e_hrsLeft - hrsLeft)*60);

		$( '#countdown' ).html( daysLeft + " päivää, " + hrsLeft +" tuntia, " + minsLeft + " minuuttia" )
	}

	/* store +/- buttons */

	/* add +/- buttons */
	$( '.store-items input' ).each( function() {
		$( this ).after('

+

-

');
	});

	/* increase and decrease buttton logic  */
	$(".store-items .inc-dec-btn div").click(function() {
	    var $button = $( this );
	    var input = $button.parent().parent().find( "input" );
	    var oldValue = input.val();
	    var newVal = oldValue;

	    if( input.is(':disabled') ){
	    	return;
	    }

	    if ( $button.text() == "+" ) {
		 	var newVal = parseFloat( oldValue ) + 1;
		} else {
		  	// Don't allow decrementing below zero
		  	if ( oldValue >= 1 ) {
		     	var newVal = parseFloat( oldValue ) - 1;
		  	}
		}
		input.val( newVal );
		filterNewStoreAmount( input );
		storeSum();
	});

	$( ".store-items input" ).change(function() {
		filterNewStoreAmount( $( this ) );
		storeSum();
	});

	function filterNewStoreAmount( input ){
		if( isNaN( parseFloat( input.val() ) ) ){
			// if new value is not a number, force it to be 0
			input.val( 0 );
		}
		else if( parseInt( input.val() ) > parseInt(input.attr('data-maxvalue')) ){
			input.val( input.attr('data-maxvalue') );
		}
		else if( parseInt( input.val() ) < 0 ){
			input.val( 0 );
		}

		input.val( parseInt( input.val(), 10 ) );
	}

	/* count money sum of store items  */
	function storeSum(){
		var storeSum = 0;
		$( ".store-items input" ).each( function() {
			// amount * price
			storeSum += parseFloat( $( this ).val() ) * parseFloat( $( this ).parent().parent().find('.item-price').html() );
		});
		$( '.store-sum span' ).html( storeSum.toFixed(2) );
	}
	$('.store-items').append('
Yhteensä:  €
');
	storeSum();

	/* fancybox for images */
	$('.item-image').wrap( function(){
		return '';
	});
	$(".fancybox").fancybox();

});
<commit_msg>store: Fix NaN if amount field is float.<commit_after>$(document).ready(function (){

	/* mobile nav button */
	$( 'nav .nav-btn' ).click( function(){
		var page_header_nav_ul = $( '#page-header nav ul' );

		page_header_nav_ul.toggleClass( 'active' );

		if( page_header_nav_ul.hasClass('active') ){
			$('html, body').animate({
				scrollTop: $("#page-header nav").offset().top
			}, 400);
			console.log('ok');
		}
	});

	/* Countdown */
	countDown( new Date("March 1, 2013 18:00:00") );
	setInterval( function() { countDown( new Date("March 1, 2013 18:00:00") ); }, 1000*60 );
	function countDown( date ){
		var today = new Date();
		msPerDay = 24 * 60 * 60 * 1000 ;
		timeLeft = (date.getTime() - today.getTime());
		var e_daysLeft = timeLeft / msPerDay;
		daysLeft = Math.floor(e_daysLeft);
		var e_hrsLeft = (e_daysLeft - daysLeft)*24;
		hrsLeft = Math.floor(e_hrsLeft);
		var minsLeft = Math.floor((e_hrsLeft - hrsLeft)*60);

		$( '#countdown' ).html( daysLeft + " päivää, " + hrsLeft +" tuntia, " + minsLeft + " minuuttia" )
	}

	/* store +/- buttons */

	/* add +/- buttons */
	$( '.store-items input' ).each( function() {
		$( this ).after('

+

-

');
	});

	/* increase and decrease buttton logic  */
	$(".store-items .inc-dec-btn div").click(function() {
	    var $button = $( this );
	    var input = $button.parent().parent().find( "input" );
	    var oldValue = input.val();
	    var newVal = oldValue;

	    if( input.is(':disabled') ){
	    	return;
	    }

	    if ( $button.text() == "+" ) {
		 	var newVal = parseFloat( oldValue ) + 1;
		} else {
		  	// Don't allow decrementing below zero
		  	if ( oldValue >= 1 ) {
		     	var newVal = parseFloat( oldValue ) - 1;
		  	}
		}
		input.val( newVal );
		filterNewStoreAmount( input );
		storeSum();
	});

	$( ".store-items input" ).change(function() {
		filterNewStoreAmount( $( this ) );
		storeSum();
	});

	function filterNewStoreAmount( input ){
		if( isNaN( parseInt( input.val() ) ) ){
			// if new value is not a number, force it to be 0
			input.val( 0 );
		}
		else if( parseInt( input.val() ) > parseInt(input.attr('data-maxvalue')) ){
			input.val( input.attr('data-maxvalue') );
		}
		else if( parseInt( input.val() ) < 0 ){
			input.val( 0 );
		}

		input.val( parseInt( input.val(), 10 ) );
	}

	/* count money sum of store items  */
	function storeSum(){
		var storeSum = 0;
		$( ".store-items input" ).each( function() {
			// amount * price
			storeSum += parseFloat( $( this ).val() ) * parseFloat( $( this ).parent().parent().find('.item-price').html() );
		});
		$( '.store-sum span' ).html( storeSum.toFixed(2) );
	}
	$('.store-items').append('
Yhteensä:  €
');
	storeSum();

	/* fancybox for images */
	$('.item-image').wrap( function(){
		return '';
	});
	$(".fancybox").fancybox();

});
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

var map;
var view;
var osmLayer;
var vectorSource;
var zoom = 15;
var maxInf = 15;
var numlist = ['[1]','[2]','[3]','[4]','[5]','[6]','[7]','[8]','[9]','[10]',
               '[11]','[12]','[13]','[14]','[15]'];

var dbpedia_err = '
DBpediaとの通信に失敗しました。';
var footer_dbpedia = 'DBpedia Japanese by ' +
      'DBpedia Community ' +
      'is licensed under a ' +
      '' +
      'Creative Commons 表示 - 継承 3.0 非移植 License

';
var no_inf = '
この地図の範囲の情報はありません。';
var origin = [139.741357, 35.658099]; // 日本経緯度原点
var server = 'http://ja.dbpedia.org/sparql';
var lang_filter = 'FILTER (LANG(?name)=\'ja\' && LANG(?abstract)=\'ja\')';
var req;

$(function(){

  $.ajaxSetup({
    timeout: 6000
  });

  view = new ol.View( {
    minZoom: 11
  } );
  map = new ol.Map({
    target: 'map',
    view: view,
    controls: new ol.control.defaults({rotate:false, attributionOptions:({collapsible: false})}).extend([
      new ol.control.ScaleLine()
    ]),
    interactions: ol.interaction.defaults({pinchRotate:false})
  });
  osmLayer = new ol.layer.Tile({
    source: new ol.source.XYZ({
      attributions: [
        new ol.Attribution({
          html: '国土地理院'
        })
      ],
      projection: 'EPSG:3857',
      url: 'http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'
    })
  });
  map.addLayer(osmLayer);
  map.on('moveend', function() {
    showContent();
  });

  vectorSource = new ol.source.Vector();
  var markerLayer = new ol.layer.Vector( {
    source: vectorSource
  });
  map.addLayer(markerLayer);

  view.setCenter(ol.proj.transform(origin, 'EPSG:4326', 'EPSG:3857'));
  view.setZoom(zoom);

  navigator.geolocation.getCurrentPosition(
    function(pos) {
      view.setCenter(
        ol.proj.transform([pos.coords.longitude, pos.coords.latitude],
          'EPSG:4326', 'EPSG:3857'
        )
      );
      view.setZoom(zoom);
    },
    function(err) {
      // do nothing
    },
    {maximumAge: 5000, timeout: 3000, enableHighAccuracy: true}
  );
});

function showContent() {

  if(req) {
    req.abort();
  }
  var zoom = view.getZoom();
  var rect = getRect();
  var sparql =
    'SELECT distinct ?name, ?abstract, ?lat, ?lon, ?url, ?link ' +
    'WHERE { ' +
    '?s rdfs:label ?name ; ' +
    'dbpedia-owl:abstract ?abstract ; ' +
    'foaf:isPrimaryTopicOf ?url ; ' +
    'geo:lat ?lat ; ' +
    'geo:long ?lon . ' +
    'OPTIONAL { ?s foaf:homepage ?link } ' +
    'FILTER ( ?lon > "' + rect[0] + '"^^xsd:float && ?lon < "' + rect[2] + '"^^xsd:float && ' +
    '?lat > "' + rect[1] + '"^^xsd:float && ?lat < "' + rect[3] + '"^^xsd:float ) ' +
    lang_filter +
    '} ' +
    'LIMIT ' + maxInf;
  var query = {
    query : sparql,
    format: 'application/sparql-results+json'
  };

  req = $.getJSON(server, query, function(data){

    req = null;
    vectorSource.clear();
    var list = data.results.bindings;

    bodydiv.innerHTML = '';             // clear
    for(i=0 ; i<list.length ; i++) {
      var topic = '' + numlist[i] + ' ' +
        '' +
        list[i].name.value + '

';
      bodydiv.innerHTML += topic;

      bodydiv.innerHTML += '
' + list[i].abstract.value + '
';

      if(list[i].link) {
        bodydiv.innerHTML += '
' +
          '' +
        list[i].link.value + '
';
      }

      var iconFeature = new ol.Feature({
      geometry: new ol.geom.Point(
        ol.proj.transform(
          [parseFloat(list[i].lon.value), parseFloat(list[i].lat.value)],
          'EPSG:4326', 'EPSG:3857'))
      });

      var iconStyle = new ol.style.Style({
        text: new ol.style.Text({
          fill: new ol.style.Fill({
            color: '#FFF',
            opacity: 0.5}),
          stroke: new ol.style.Stroke({
            color: '#00F',
            opacity: 0.5,
            width: 5}),
          text: i+1,
          opacity: 0.5,
          font: '20px Verdana'
        }),
        opacity: 0.5
      });

      iconFeature.setStyle(iconStyle);
      vectorSource.addFeature(iconFeature);

    }
    if(list.length > 0) {
      bodydiv.innerHTML += '
' + footer_dbpedia;
    } else {
      bodydiv.innerHTML = no_inf;
    }

  })
  .error(function() {
    req = null;
    vectorSource.clear();
    bodydiv.innerHTML = dbpedia_err;
  });

  $('#contentdiv').scrollTop(0);

}

function getRect() {
  var lon1 = ol.proj.transform(map.getCoordinateFromPixel([0, 0]),
          'EPSG:3857', 'EPSG:4326')[0];
  var lat1 = ol.proj.transform(map.getCoordinateFromPixel([0, 0]),
          'EPSG:3857', 'EPSG:4326')[1];
  var lon2 = ol.proj.transform(map.getCoordinateFromPixel(map.getSize()),
          'EPSG:3857', 'EPSG:4326')[0];
  var lat2 = ol.proj.transform(map.getCoordinateFromPixel(map.getSize()),
          'EPSG:3857', 'EPSG:4326')[1];
  var minLon = lon1 < lon2 ? lon1 : lon2;
  var maxLon = lon1 > lon2 ? lon1 : lon2;
  var minLat = lat1 < lat2 ? lat1 : lat2;
  var maxLat = lat1 > lat2 ? lat1 : lat2;
  return new Array(minLon, minLat, maxLon, maxLat);
}
<commit_msg>use Wikidata instead of DBpedia to enable https<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

var map;
var view;
var osmLayer;
var vectorSource;
var zoom = 15;
var maxInf = 20;
var numlist = ['[1]','[2]','[3]','[4]','[5]','[6]','[7]','[8]','[9]','[10]',
               '[11]','[12]','[13]','[14]','[15]','[16]','[17]','[18]','[19]','[20]'];

var dbpedia_err = '
Wikidataとの通信に失敗しました。';
var no_inf = '
この地図の範囲の情報はありません。';
var origin = [139.741357, 35.658099]; // 日本経緯度原点
var server = 'https://query.wikidata.org/sparql';
var req;

$(function(){

  $.ajaxSetup({
    timeout: 6000
  });

  view = new ol.View( {
    minZoom: 11
  } );
  map = new ol.Map({
    target: 'map',
    view: view,
    controls: new ol.control.defaults({rotate:false, attributionOptions:({collapsible: false})}).extend([
      new ol.control.ScaleLine()
    ]),
    interactions: ol.interaction.defaults({pinchRotate:false})
  });
  osmLayer = new ol.layer.Tile({
    source: new ol.source.XYZ({
      attributions: [
        new ol.Attribution({
          html: '国土地理院'
        })
      ],
      projection: 'EPSG:3857',
      url: 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'
    })
  });
  map.addLayer(osmLayer);
  map.on('moveend', function() {
    showContent();
  });

  vectorSource = new ol.source.Vector();
  var markerLayer = new ol.layer.Vector( {
    source: vectorSource
  });
  map.addLayer(markerLayer);

  view.setCenter(ol.proj.transform(origin, 'EPSG:4326', 'EPSG:3857'));
  view.setZoom(zoom);

  navigator.geolocation.getCurrentPosition(
    function(pos) {
      view.setCenter(
        ol.proj.transform([pos.coords.longitude, pos.coords.latitude],
          'EPSG:4326', 'EPSG:3857'
        )
      );
      view.setZoom(zoom);
    },
    function(err) {
      // do nothing
    },
    {maximumAge: 5000, timeout: 3000, enableHighAccuracy: true}
  );
});

function showContent() {

  if(req) {
    req.abort();
  }
  var zoom = view.getZoom();
  var rect = getRect();
  var sparql =
    'SELECT DISTINCT ?sLabel ?lat ?lon ?dsc ?wp WHERE { ' +
    '  ?wp schema:about ?s . ' +
    '  ?wp schema:inLanguage "ja" . ' +
    '  FILTER (SUBSTR(str(?wp), 1, 25) = "https://ja.wikipedia.org/") ' +
    '  OPTIONAL { ' +
    '    ?s schema:description ?dsc . ' +
    '    FILTER (lang(?dsc) = "ja") ' +
    '  } ' +
    '  SERVICE wikibase:box { ' +
    '    ?s wdt:P625 ?location . ' +
    '    bd:serviceParam wikibase:cornerSouthWest "Point(' +
           rect[0] + ' ' +  rect[1] +
    '    )"^^geo:wktLiteral . ' +
    '    bd:serviceParam wikibase:cornerNorthEast "Point(' +
           rect[2] + ' ' + rect[3] +
    '    )"^^geo:wktLiteral . ' +
    '  } ' +
    '  BIND(geof:latitude(?location) as ?lat) ' +
    '  BIND(geof:longitude(?location) as ?lon) ' +
    '  SERVICE wikibase:label { ' +
    '    bd:serviceParam wikibase:language "ja, en" . ' +
    '  } ' +
    '} ' +
    'LIMIT ' + maxInf;
  var query = {
    query : sparql,
    format: 'json'
  };

  req = $.getJSON(server, query, function(data){

    req = null;
    vectorSource.clear();
    var list = data.results.bindings;

    bodydiv.innerHTML = '';             // clear
    for(i=0 ; i<list.length ; i++) {
      var topic = '' + numlist[i] + ' ' +
        '' +
        list[i].sLabel.value + '

';
      bodydiv.innerHTML += topic;

      if(list[i].dsc) {
        bodydiv.innerHTML += '
' + list[i].dsc.value + '
';
      }

      var iconFeature = new ol.Feature({
      geometry: new ol.geom.Point(
        ol.proj.transform(
          [parseFloat(list[i].lon.value), parseFloat(list[i].lat.value)],
          'EPSG:4326', 'EPSG:3857'))
      });

      var iconStyle = new ol.style.Style({
        text: new ol.style.Text({
          fill: new ol.style.Fill({
            color: '#FFF',
            opacity: 0.5}),
          stroke: new ol.style.Stroke({
            color: '#00F',
            opacity: 0.5,
            width: 5}),
          text: i+1,
          opacity: 0.5,
          font: '20px Verdana'
        }),
        opacity: 0.5
      });

      iconFeature.setStyle(iconStyle);
      vectorSource.addFeature(iconFeature);

    }

  })
  .error(function(jqXHR, textStatus, errorThrown) {
    req = null;
    vectorSource.clear();
//    bodydiv.innerHTML = "エラー：" + textStatus + "
テキスト：" + jqXHR.responseText;
    bodydiv.innerHTML = "";
  });

  $('#contentdiv').scrollTop(0);

}

function getRect() {
  var lon1 = ol.proj.transform(map.getCoordinateFromPixel([0, 0]),
          'EPSG:3857', 'EPSG:4326')[0];
  var lat1 = ol.proj.transform(map.getCoordinateFromPixel([0, 0]),
          'EPSG:3857', 'EPSG:4326')[1];
  var lon2 = ol.proj.transform(map.getCoordinateFromPixel(map.getSize()),
          'EPSG:3857', 'EPSG:4326')[0];
  var lat2 = ol.proj.transform(map.getCoordinateFromPixel(map.getSize()),
          'EPSG:3857', 'EPSG:4326')[1];
  var minLon = lon1 < lon2 ? lon1 : lon2;
  var maxLon = lon1 > lon2 ? lon1 : lon2;
  var minLat = lat1 < lat2 ? lat1 : lat2;
  var maxLat = lat1 > lat2 ? lat1 : lat2;
  return new Array(minLon, minLat, maxLon, maxLat);
}
<|endoftext|>