<commit_before>pc.programlib = {
    getSnippet: function (device, id) {
        var code = '';

        switch (id) {
             case 'common_main_begin':
                code +=    'void main(void)\n{\n';
                break;

            case 'common_main_end':
                code += '}\n';
                break;

            //////////////////////////////
            // FRAGMENT SHADER SNIPPETS //
            //////////////////////////////
            case 'fs_alpha_test_decl':
                code += 'uniform float alpha_ref;\n';
                break;

            case 'fs_alpha_test':
                code += '    if (gl_FragColor.a < alpha_ref) discard;\n\n';
                break;

            case 'fs_clamp':
                code += '    gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);\n';
                break;

            case 'fs_flat_color_decl':
                code += 'uniform vec4 uColor;\n';
                break;

            case 'fs_flat_color':
                code += '    gl_FragColor = uColor;\n';
                break;

            case 'fs_fog_linear_decl':
            case 'fs_fog_exp_decl':
            case 'fs_fog_exp2_decl':
                code += 'uniform vec3 fog_color;\n';
                if (id === 'fs_fog_linear_decl') {
                    code += 'uniform float fog_start;\n';
                    code += 'uniform float fog_end;\n\n';
                } else {
                    code += 'uniform float fog_density;\n\n';
                }
                break;

            case 'fs_fog_linear':
            case 'fs_fog_exp':
            case 'fs_fog_exp2':
                code += '    float depth = gl_FragCoord.z / gl_FragCoord.w;\n';
                if (id === 'fs_fog_linear') {
                    code += '    float fogFactor = (fog_end - depth) / (fog_end - fog_start);\n';
                } else if (id === 'fs_fog_exp') {
                    code += '    float fogFactor = exp(-depth * fog_density);\n';
                } else {
                    code += '    float fogFactor = exp(-depth * depth * fog_density * fog_density);\n';
                }
                code += '    fogFactor = clamp(fogFactor, 0.0, 1.0);\n';
                code += '    gl_FragColor.rgb = mix(fog_color, gl_FragColor.rgb, fogFactor);\n';
                break;

            case 'fs_precision':
                code += 'precision ' + device.precision + ' float;\n\n';
                break;

            case 'fs_height_map_funcs':
                code += 'vec3 perturb_normal( vec3 N, vec3 p, vec2 uv )\n';
                code += '{\n';
                code += '    vec3 dp1 = dFdx( p );\n';
                code += '    vec3 dp2 = dFdy( p );\n';
                code += '    vec2 duv1 = dFdx( uv );\n';
                code += '    vec2 duv2 = dFdy( uv );\n\n';

                code += '    vec3 dp2perp = cross( dp2, N );\n';
                code += '    vec3 dp1perp = cross( N, dp1 );\n\n';

                code += '    const float bumpScale = 0.125;\n';
                code += '    float Hll = bumpScale * texture2D( texture_heightMap, uv ).x;\n';
                code += '    float dBx = bumpScale * texture2D( texture_heightMap, uv + duv1 ).x - Hll;\n';
                code += '    float dBy = bumpScale * texture2D( texture_heightMap, uv + duv2 ).x - Hll;\n\n';

                code += '    float fDet = dot( dp1, dp2perp );\n';
                code += '    vec3 vGrad = sign( fDet ) * ( dBx * dp2perp + dBy * dp1perp );\n';
                code += '    return normalize( abs( fDet ) * N - vGrad );\n';
                code += '}\n\n';
                break;

            case 'fs_normal_map_funcs':
                if (!pc.precalculatedTangents) {
                    code += 'mat3 cotangent_frame( vec3 N, vec3 p, vec2 uv )\n';
                    code += '{\n';
                                 // get edge vectors of the pixel triangle
                    code += '    vec3 dp1 = dFdx( p );\n';
                    code += '    vec3 dp2 = dFdy( p );\n';
                    code += '    vec2 duv1 = dFdx( uv );\n';
                    code += '    vec2 duv2 = dFdy( uv );\n\n';

                                 // solve the linear system
                    code += '    vec3 dp2perp = cross( dp2, N );\n';
                    code += '    vec3 dp1perp = cross( N, dp1 );\n';
                    code += '    vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;\n';
                    code += '    vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;\n\n';

                                 // construct a scale-invariant frame
                    code += '    float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );\n';
                    code += '    return mat3( T * invmax, B * invmax, N );\n';
                    code += '}\n\n';

                    code += 'vec3 perturb_normal( vec3 N, vec3 V, vec2 uv )\n';
                    code += '{\n';
                                 // assume N, the interpolated vertex normal and
                                 // V, the view vector (vertex to eye)
                    code += '    vec3 map = texture2D( texture_normalMap, uv ).xyz;\n';
                    code += '    map = map * 255./127. - 128./127.;\n';
                    code += '    map.xy = map.xy * material_bumpiness;\n';
                    code += '    mat3 TBN = cotangent_frame( N, -V, uv );\n';
                    code += '    return normalize( TBN * map );\n';
                    code += '}\n\n';
                }
                break;

            ////////////////////////////
            // VERTEX SHADER SNIPPETS //
            ////////////////////////////
            case 'vs_skin_decl':
                if (device.supportsBoneTextures) {
                    code += [
                        'attribute vec4 vertex_boneWeights;',
                        'attribute vec4 vertex_boneIndices;',
                        '',
                        'uniform sampler2D texture_poseMap;',
                        'uniform vec2 texture_poseMapSize;',
                        'uniform vec3 skinPosOffset;',
                        '',
                        'mat4 getBoneMatrix(const in float i)',
                        '{',
                        '    float j = i * 4.0;',
                        '    float x = mod(j, float(texture_poseMapSize.x));',
                        '    float y = floor(j / float(texture_poseMapSize.x));',
                        '',
                        '    float dx = 1.0 / float(texture_poseMapSize.x);',
                        '    float dy = 1.0 / float(texture_poseMapSize.y);',
                        '',
                        '    y = dy * (y + 0.5);',
                        '',
                        '    vec4 v1 = texture2D(texture_poseMap, vec2(dx * (x + 0.5), y));',
                        '    vec4 v2 = texture2D(texture_poseMap, vec2(dx * (x + 1.5), y));',
                        '    vec4 v3 = texture2D(texture_poseMap, vec2(dx * (x + 2.5), y));',
                        '    vec4 v4 = texture2D(texture_poseMap, vec2(dx * (x + 3.5), y));',
                        '',
                        '    mat4 bone = mat4(v1, v2, v3, v4);',
                        '',
                        '    return bone;',
                        '}'
                    ].join('\n');
                } else {
                    code += [
                        'attribute vec4 vertex_boneWeights;',
                        'attribute vec4 vertex_boneIndices;',
                        '',
                        'uniform mat4 matrix_pose[' + device.getBoneLimit() + '];',
                        '',
                        'mat4 getBoneMatrix(const in float i)',
                        '{',
                        '    mat4 bone = matrix_pose[int(i)];',
                        '',
                        '    return bone;',
                        '}'
                    ].join('\n');
                }
                code += '\n\n';
                break;

            case 'vs_transform_decl':
                code += 'attribute vec3 vertex_position;\n';
                code += 'uniform mat4 matrix_model;\n';
                code += 'uniform mat4 matrix_viewProjection;\n\n';
                break;
        }

        return code;
    },

    gammaCode: function (value) {
        return value===pc.GAMMA_NONE? pc.shaderChunks.gamma1_0PS :
              (value===pc.GAMMA_SRGBFAST? pc.shaderChunks.gamma2_2FastPS : pc.shaderChunks.gamma2_2PS);
    },

    tonemapCode: function (value) {
        return value ? pc.shaderChunks.tonemappingFilmicPS : pc.shaderChunks.tonemappingLinearPS;
    }

};
<commit_msg>[FIX] Fix skinned shader compilation for non-bone texture path<commit_after>pc.programlib = {
    getSnippet: function (device, id) {
        var code = '';

        switch (id) {
             case 'common_main_begin':
                code +=    'void main(void)\n{\n';
                break;

            case 'common_main_end':
                code += '}\n';
                break;

            //////////////////////////////
            // FRAGMENT SHADER SNIPPETS //
            //////////////////////////////
            case 'fs_alpha_test_decl':
                code += 'uniform float alpha_ref;\n';
                break;

            case 'fs_alpha_test':
                code += '    if (gl_FragColor.a < alpha_ref) discard;\n\n';
                break;

            case 'fs_clamp':
                code += '    gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);\n';
                break;

            case 'fs_flat_color_decl':
                code += 'uniform vec4 uColor;\n';
                break;

            case 'fs_flat_color':
                code += '    gl_FragColor = uColor;\n';
                break;

            case 'fs_fog_linear_decl':
            case 'fs_fog_exp_decl':
            case 'fs_fog_exp2_decl':
                code += 'uniform vec3 fog_color;\n';
                if (id === 'fs_fog_linear_decl') {
                    code += 'uniform float fog_start;\n';
                    code += 'uniform float fog_end;\n\n';
                } else {
                    code += 'uniform float fog_density;\n\n';
                }
                break;

            case 'fs_fog_linear':
            case 'fs_fog_exp':
            case 'fs_fog_exp2':
                code += '    float depth = gl_FragCoord.z / gl_FragCoord.w;\n';
                if (id === 'fs_fog_linear') {
                    code += '    float fogFactor = (fog_end - depth) / (fog_end - fog_start);\n';
                } else if (id === 'fs_fog_exp') {
                    code += '    float fogFactor = exp(-depth * fog_density);\n';
                } else {
                    code += '    float fogFactor = exp(-depth * depth * fog_density * fog_density);\n';
                }
                code += '    fogFactor = clamp(fogFactor, 0.0, 1.0);\n';
                code += '    gl_FragColor.rgb = mix(fog_color, gl_FragColor.rgb, fogFactor);\n';
                break;

            case 'fs_precision':
                code += 'precision ' + device.precision + ' float;\n\n';
                break;

            case 'fs_height_map_funcs':
                code += 'vec3 perturb_normal( vec3 N, vec3 p, vec2 uv )\n';
                code += '{\n';
                code += '    vec3 dp1 = dFdx( p );\n';
                code += '    vec3 dp2 = dFdy( p );\n';
                code += '    vec2 duv1 = dFdx( uv );\n';
                code += '    vec2 duv2 = dFdy( uv );\n\n';

                code += '    vec3 dp2perp = cross( dp2, N );\n';
                code += '    vec3 dp1perp = cross( N, dp1 );\n\n';

                code += '    const float bumpScale = 0.125;\n';
                code += '    float Hll = bumpScale * texture2D( texture_heightMap, uv ).x;\n';
                code += '    float dBx = bumpScale * texture2D( texture_heightMap, uv + duv1 ).x - Hll;\n';
                code += '    float dBy = bumpScale * texture2D( texture_heightMap, uv + duv2 ).x - Hll;\n\n';

                code += '    float fDet = dot( dp1, dp2perp );\n';
                code += '    vec3 vGrad = sign( fDet ) * ( dBx * dp2perp + dBy * dp1perp );\n';
                code += '    return normalize( abs( fDet ) * N - vGrad );\n';
                code += '}\n\n';
                break;

            case 'fs_normal_map_funcs':
                if (!pc.precalculatedTangents) {
                    code += 'mat3 cotangent_frame( vec3 N, vec3 p, vec2 uv )\n';
                    code += '{\n';
                                 // get edge vectors of the pixel triangle
                    code += '    vec3 dp1 = dFdx( p );\n';
                    code += '    vec3 dp2 = dFdy( p );\n';
                    code += '    vec2 duv1 = dFdx( uv );\n';
                    code += '    vec2 duv2 = dFdy( uv );\n\n';

                                 // solve the linear system
                    code += '    vec3 dp2perp = cross( dp2, N );\n';
                    code += '    vec3 dp1perp = cross( N, dp1 );\n';
                    code += '    vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;\n';
                    code += '    vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;\n\n';

                                 // construct a scale-invariant frame
                    code += '    float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );\n';
                    code += '    return mat3( T * invmax, B * invmax, N );\n';
                    code += '}\n\n';

                    code += 'vec3 perturb_normal( vec3 N, vec3 V, vec2 uv )\n';
                    code += '{\n';
                                 // assume N, the interpolated vertex normal and
                                 // V, the view vector (vertex to eye)
                    code += '    vec3 map = texture2D( texture_normalMap, uv ).xyz;\n';
                    code += '    map = map * 255./127. - 128./127.;\n';
                    code += '    map.xy = map.xy * material_bumpiness;\n';
                    code += '    mat3 TBN = cotangent_frame( N, -V, uv );\n';
                    code += '    return normalize( TBN * map );\n';
                    code += '}\n\n';
                }
                break;

            ////////////////////////////
            // VERTEX SHADER SNIPPETS //
            ////////////////////////////
            case 'vs_skin_decl':
                if (device.supportsBoneTextures) {
                    code += [
                        'attribute vec4 vertex_boneWeights;',
                        'attribute vec4 vertex_boneIndices;',
                        '',
                        'uniform sampler2D texture_poseMap;',
                        'uniform vec2 texture_poseMapSize;',
                        'uniform vec3 skinPosOffset;',
                        '',
                        'mat4 getBoneMatrix(const in float i)',
                        '{',
                        '    float j = i * 4.0;',
                        '    float x = mod(j, float(texture_poseMapSize.x));',
                        '    float y = floor(j / float(texture_poseMapSize.x));',
                        '',
                        '    float dx = 1.0 / float(texture_poseMapSize.x);',
                        '    float dy = 1.0 / float(texture_poseMapSize.y);',
                        '',
                        '    y = dy * (y + 0.5);',
                        '',
                        '    vec4 v1 = texture2D(texture_poseMap, vec2(dx * (x + 0.5), y));',
                        '    vec4 v2 = texture2D(texture_poseMap, vec2(dx * (x + 1.5), y));',
                        '    vec4 v3 = texture2D(texture_poseMap, vec2(dx * (x + 2.5), y));',
                        '    vec4 v4 = texture2D(texture_poseMap, vec2(dx * (x + 3.5), y));',
                        '',
                        '    mat4 bone = mat4(v1, v2, v3, v4);',
                        '',
                        '    return bone;',
                        '}'
                    ].join('\n');
                } else {
                    code += [
                        'attribute vec4 vertex_boneWeights;',
                        'attribute vec4 vertex_boneIndices;',
                        '',
                        'uniform mat4 matrix_pose[' + device.getBoneLimit() + '];',
                        'uniform vec3 skinPosOffset;',
                        '',
                        'mat4 getBoneMatrix(const in float i)',
                        '{',
                        '    mat4 bone = matrix_pose[int(i)];',
                        '',
                        '    return bone;',
                        '}'
                    ].join('\n');
                }
                code += '\n\n';
                break;

            case 'vs_transform_decl':
                code += 'attribute vec3 vertex_position;\n';
                code += 'uniform mat4 matrix_model;\n';
                code += 'uniform mat4 matrix_viewProjection;\n\n';
                break;
        }

        return code;
    },

    gammaCode: function (value) {
        return value===pc.GAMMA_NONE? pc.shaderChunks.gamma1_0PS :
              (value===pc.GAMMA_SRGBFAST? pc.shaderChunks.gamma2_2FastPS : pc.shaderChunks.gamma2_2PS);
    },

    tonemapCode: function (value) {
        return value ? pc.shaderChunks.tonemappingFilmicPS : pc.shaderChunks.tonemappingLinearPS;
    }

};
<|endoftext|>


<commit_before>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function($) {
	/**
	 * yiiactiveform set function.
	 * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function(options) {
		return this.each(function() {
			var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {});
			var $form = $(this);
			var id = $form.attr('id');
			if(settings.validationUrl == undefined)
				settings.validationUrl = $form.attr('action');
			$.each(settings.attributes, function(i,attribute){
				settings.attributes[i] = $.extend({
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					beforeValidateAttribute : settings.beforeValidateAttribute,
					afterValidateAttribute : settings.afterValidateAttribute,
					validatingCssClass : settings.validatingCssClass
				}, attribute);
				settings.attributes[i].value = $('#'+attribute.inputID, $form).val();
			});
			$(this).data('settings', settings);

			settings.submitting=false;  // whether it is waiting for ajax submission result
			var validate = function(attribute, forceValidate) {
				if (forceValidate)
					attribute.status = 2;
				$.each(settings.attributes, function(){
					if (this.value != $('#'+this.inputID, $form).val()) {
						this.status = 2;
						forceValidate = true;
					}
				});
				if (!forceValidate)
					return;

				if(settings.timer!=undefined) {
					clearTimeout(settings.timer);
				}

				settings.timer = setTimeout(function(){
					if(settings.submitting || $form.is(':hidden'))
						return;
					if(attribute.beforeValidateAttribute==undefined || attribute.beforeValidateAttribute($form, attribute)) {
						$.each(settings.attributes, function(){
							if (this.status == 2) {
								this.status = 3;
								$.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass);
							}
						});
						$.fn.yiiactiveform.validate($form, function(data) {
							var hasError=false;
							$.each(settings.attributes, function(){
								if (this.status == 2 || this.status == 3) {
									hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
								}
							});
							if(attribute.afterValidateAttribute!=undefined) {
								attribute.afterValidateAttribute($form,attribute,data,hasError);
							}
						});
					}
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function(i, attribute) {
				if (attribute.validateOnChange) {
					$('#'+attribute.inputID, $form).change(function(){
						var inputType = $('#'+attribute.inputID).attr('type');
						validate(attribute, inputType=='checkbox' || inputType=='radio');
					}).blur(function(){
						if(attribute.status!=2 && attribute.status!=3)
							validate(attribute, !attribute.status);
					});
				}
				if (attribute.validateOnType) {
					$('#'+attribute.inputID, $form).keyup(function(){
						if (attribute.value != $('#'+attribute.inputID, $form).val())
							validate(attribute, false);
					});
				}
			});

			if (settings.validateOnSubmit) {
				$form.find(':submit').live('mouseup keyup',function(){
					$form.data('submitObject',$(this));
				});
				var validated = false;
				$form.submit(function(){
					if (validated)
						return true;
					if(settings.timer!=undefined) {
						clearTimeout(settings.timer);
					}
					settings.submitting=true;
					if(settings.beforeValidate==undefined || settings.beforeValidate($form)) {
						$.fn.yiiactiveform.validate($form, function(data){
							var hasError = false;
							$.each(settings.attributes, function(i, attribute){
								hasError = $.fn.yiiactiveform.updateInput(attribute, data, $form) || hasError;
							});
							$.fn.yiiactiveform.updateSummary($form, data);
							if(settings.afterValidate==undefined || settings.afterValidate($form, data, hasError)) {
								if(!hasError) {
									validated = true;
									var $button = $form.data('submitObject') || $form.find(':submit:first');
									// TODO: if the submission is caused by "change" event, it will not work
									if ($button.length)
										$button.click();
									else  // no submit button in the form
										$form.submit();
									return false;
								}
							}
							settings.submitting=false;
						});
					}
					else {
						settings.submitting=false;
					}
					return false;
				});
			}

			/*
			 * In case of reseting the form we need to reset error messages
			 * NOTE1: $form.reset - does not exist
			 * NOTE2: $form.live('reset',...) does not work
			 */
			$form.bind('reset',function(){
				/*
				 * because we bind directly to a form reset event, not to a reset button (that could or could not exist),
				 * when this function is executed form elements values have not been reset yet,
				 * because of that we use the setTimeout
				 */
				setTimeout(function(){
					$.each(settings.attributes, function(i, attribute){
						attribute.status = 0;
						var $error = $('#'+attribute.errorID, $form);
						var $container = $.fn.yiiactiveform.getInputContainer(attribute, $form);

						$container
							.removeClass(attribute.validatingCssClass)
							.removeClass(attribute.errorCssClass)
							.removeClass(attribute.successCssClass);

						$error.html('').hide();

						/*
						 * without the setTimeout() call val() would return the entered value instead of the reseted value
						 */
						attribute.value = $('#'+attribute.inputID, $form).val();

						/*
						 * If the form is submited (non ajax) with errors, labels and input gets the class 'error'
						 */
						$('label,input',$form).each(function(){
							$(this).removeClass('error');
						});
					});
					$('#'+settings.summaryID+' ul').html('');
					$('#'+settings.summaryID).hide();
					//.. set to initial focus on reset
					if(settings.focus != undefined && !window.location.hash)
						$(settings.focus).focus();
				},1);
			});

			/*
			 * set to initial focus
			 */
			if(settings.focus != undefined && !window.location.hash)
				$(settings.focus).focus();
		});
	};

	/**
	 * Returns the container element of the specified attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param form the form jQuery object
	 * @return jquery the jquery representation of the container
	 */
	$.fn.yiiactiveform.getInputContainer = function(attribute, form) {
		if(attribute.inputContainer == undefined)
			return $('#'+attribute.inputID, form).closest('div');
		else
			return $(attribute.inputContainer).filter(':has("#'+attribute.inputID+'")');
	};

	/**
	 * updates the error message and the input container for a particular attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param messages array the json data obtained from the ajax validation request
	 * @param form the form jQuery object
	 * @return boolean whether there is a validation error for the specified attribute
	 */
	$.fn.yiiactiveform.updateInput = function(attribute, messages, form) {
		attribute.status = 1;
		var hasError = messages!=null && $.isArray(messages[attribute.id]) && messages[attribute.id].length>0;
		var $error = $('#'+attribute.errorID, form);
		var $container = $.fn.yiiactiveform.getInputContainer(attribute, form);
		$container.removeClass(attribute.validatingCssClass)
			.removeClass(attribute.errorCssClass)
			.removeClass(attribute.successCssClass);

		if(hasError) {
			$error.html(messages[attribute.id][0]);
			$container.addClass(attribute.errorCssClass);
		}
		else if(attribute.enableAjaxValidation || attribute.clientValidation) {
			$container.addClass(attribute.successCssClass);
		}
		if(!attribute.hideErrorMessage)
			$error.toggle(hasError);

		attribute.value = $('#'+attribute.inputID, form).val();

		return hasError;
	};

	/**
	 * updates the error summary, if any.
	 * @param form jquery the jquery representation of the form
	 * @param messages array the json data obtained from the ajax validation request
	 */
	$.fn.yiiactiveform.updateSummary = function(form, messages) {
		var settings = $(form).data('settings');
		if (settings.summaryID == undefined)
			return;
		var content = '';
		$.each(settings.attributes, function(i, attribute){
			if(messages && $.isArray(messages[attribute.id])) {
				$.each(messages[attribute.id],function(j,message){
					content = content + '<li>' + message + '</li>';
				});
			}
		});
		$('#'+settings.summaryID+' ul').html(content);
		$('#'+settings.summaryID).toggle(content!='');
	};

	/**
	 * Performs the ajax validation request.
	 * This method is invoked internally to trigger the ajax validation.
	 * @param form jquery the jquery representation of the form
	 * @param successCallback function the function to be invoked if the ajax request succeeds
	 * @param errorCallback function the function to be invoked if the ajax request fails
	 */
	$.fn.yiiactiveform.validate = function(form, successCallback, errorCallback) {
		var $form = $(form);
		var settings = $form.data('settings');

		var messages = {};
		var needAjaxValidation = false;
		$.each(settings.attributes, function(){
			var msg = [];
			if (this.clientValidation != undefined && (settings.submitting || this.status == 2 || this.status == 3)) {
				var value = $('#'+this.inputID, $form).val();
				this.clientValidation(value, msg, this);
				if (msg.length) {
					messages[this.id] = msg;
				}
			}
			if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status == 2 || this.status == 3))
				needAjaxValidation = true;
		});

		if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) {
			if(settings.submitting) {
				// delay callback so that the form can be submitted without problem
				setTimeout(function(){
					successCallback(messages);
				},200);
			}
			else {
				successCallback(messages);
			}
			return;
		}

		var $button = $form.data('submitObject');
		var extData = '&'+settings.ajaxVar+'='+$form.attr('id');
		if($button && $button.length)
			extData += '&'+$button.attr('name')+'='+$button.attr('value');

		$.ajax({
			url : settings.validationUrl,
			type : $form.attr('method'),
			data : $form.serialize()+extData,
			dataType : 'json',
			success : function(data) {
				if (data != null && typeof data == 'object') {
					$.each(settings.attributes, function() {
						if (!this.enableAjaxValidation)
							delete data[this.id];
					});
					successCallback($.extend({}, messages, data));
				}
				else {
					successCallback(messages);
				}
			},
			error : function() {
				if (errorCallback!=undefined) {
					errorCallback();
				}
			}
		});
	};

	/**
	 * Returns the configuration for the specified form.
	 * The configuration contains all needed information to perform ajax-based validation.
	 * @param form jquery the jquery representation of the form
	 * @return object the configuration for the specified form.
	 */
	$.fn.yiiactiveform.getSettings = function(form) {
		return $(form).data('settings');
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 200,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		beforeValidateAttribute: undefined, // function(form, attribute) : boolean
		afterValidateAttribute: undefined,  // function(form, attribute, data, hasError)
		beforeValidate: undefined, // function(form) : boolean
		afterValidate: undefined,  // function(form, data, hasError) : boolean
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     id : 'ModelClass_attribute', // the unique attribute ID
		 *     model : 'ModelClass', // the model class name
		 *     name : 'name', // attribute name
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: empty, not entered before,  1: validated, 2: pending validation, 3: validating
		 *     focus : undefined,  // jquery selector that indicates which element to receive input focus initially
		 *     validationDelay: 200,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 *     enableAjaxValidation : true,
		 *     enableClientValidation : true,
		 *     clientValidation : undefined, // function(value, messages, attribute) : client-side validation
		 *     beforeValidateAttribute: undefined, // function(form, attribute) : boolean
		 *     afterValidateAttribute: undefined,  // function(form, attribute, data, hasError)
		 * }
		 */
		attributes : []
	};

})(jQuery);<commit_msg>few improvements to JS code in jquery.yiiactiveform.js<commit_after>/**
 * jQuery yiiactiveform plugin file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright Copyright © 2008-2010 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 * @version $Id$
 * @since 1.1.1
 */

;(function($) {
	/**
	 * yiiactiveform set function.
	 * @param options map settings for the active form plugin. Please see {@link CActiveForm::options} for availablel options.
	 */
	$.fn.yiiactiveform = function(options) {
		return this.each(function() {
			var settings = $.extend({}, $.fn.yiiactiveform.defaults, options || {});
			var $form = $(this);
			var id = $form.attr('id');
			if(settings.validationUrl == undefined)
				settings.validationUrl = $form.attr('action');
			$.each(settings.attributes, function(i,attribute){
				settings.attributes[i] = $.extend({
					validationDelay : settings.validationDelay,
					validateOnChange : settings.validateOnChange,
					validateOnType : settings.validateOnType,
					hideErrorMessage : settings.hideErrorMessage,
					inputContainer : settings.inputContainer,
					errorCssClass : settings.errorCssClass,
					successCssClass : settings.successCssClass,
					beforeValidateAttribute : settings.beforeValidateAttribute,
					afterValidateAttribute : settings.afterValidateAttribute,
					validatingCssClass : settings.validatingCssClass
				}, attribute);
				settings.attributes[i].value = $('#'+attribute.inputID, $form).val();
			});
			$(this).data('settings', settings);

			settings.submitting=false;  // whether it is waiting for ajax submission result
			var validate = function(attribute, forceValidate) {
				if (forceValidate)
					attribute.status = 2;
				$.each(settings.attributes, function(){
					if (this.value != $('#'+this.inputID, $form).val()) {
						this.status = 2;
						forceValidate = true;
					}
				});
				if (!forceValidate)
					return;

				if(settings.timer!=undefined) {
					clearTimeout(settings.timer);
				}

				settings.timer = setTimeout(function(){
					if(settings.submitting || $form.is(':hidden'))
						return;
					if(attribute.beforeValidateAttribute==undefined || attribute.beforeValidateAttribute($form, attribute)) {
						$.each(settings.attributes, function(){
							if (this.status == 2) {
								this.status = 3;
								$.fn.yiiactiveform.getInputContainer(this, $form).addClass(this.validatingCssClass);
							}
						});
						$.fn.yiiactiveform.validate($form, function(data) {
							var hasError=false;
							$.each(settings.attributes, function(){
								if (this.status == 2 || this.status == 3) {
									hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;
								}
							});
							if(attribute.afterValidateAttribute!=undefined) {
								attribute.afterValidateAttribute($form,attribute,data,hasError);
							}
						});
					}
				}, attribute.validationDelay);
			};

			$.each(settings.attributes, function(i, attribute) {
				if (attribute.validateOnChange) {
					$('#'+attribute.inputID, $form).change(function(){
						validate(attribute, this.type=='checkbox' || this.type=='radio');
					}).blur(function(){
						if(attribute.status!=2 && attribute.status!=3)
							validate(attribute, !attribute.status);
					});
				}
				if (attribute.validateOnType) {
					$('#'+attribute.inputID, $form).keyup(function(){
						if (attribute.value != $(this).val())
							validate(attribute, false);
					});
				}
			});

			if (settings.validateOnSubmit) {
				$form.find(':submit').live('mouseup keyup',function(){
					$form.data('submitObject',$(this));
				});
				var validated = false;
				$form.submit(function(){
					if (validated)
						return true;
					if(settings.timer!=undefined) {
						clearTimeout(settings.timer);
					}
					settings.submitting=true;
					if(settings.beforeValidate==undefined || settings.beforeValidate($form)) {
						$.fn.yiiactiveform.validate($form, function(data){
							var hasError = false;
							$.each(settings.attributes, function(i, attribute){
								hasError = $.fn.yiiactiveform.updateInput(attribute, data, $form) || hasError;
							});
							$.fn.yiiactiveform.updateSummary($form, data);
							if(settings.afterValidate==undefined || settings.afterValidate($form, data, hasError)) {
								if(!hasError) {
									validated = true;
									var $button = $form.data('submitObject') || $form.find(':submit:first');
									// TODO: if the submission is caused by "change" event, it will not work
									if ($button.length)
										$button.click();
									else  // no submit button in the form
										$form.submit();
									return false;
								}
							}
							settings.submitting=false;
						});
					}
					else {
						settings.submitting=false;
					}
					return false;
				});
			}

			/*
			 * In case of reseting the form we need to reset error messages
			 * NOTE1: $form.reset - does not exist
			 * NOTE2: $form.live('reset',...) does not work
			 */
			$form.bind('reset',function(){
				/*
				 * because we bind directly to a form reset event, not to a reset button (that could or could not exist),
				 * when this function is executed form elements values have not been reset yet,
				 * because of that we use the setTimeout
				 */
				setTimeout(function(){
					$.each(settings.attributes, function(i, attribute){
						attribute.status = 0;
						var $error = $('#'+attribute.errorID, $form);
						var $container = $.fn.yiiactiveform.getInputContainer(attribute, $form);

						$container.removeClass(
							attribute.validatingCssClass + ' ' +
							attribute.errorCssClass + ' ' +
							attribute.successCssClass
						);

						$error.html('').hide();

						/*
						 * without the setTimeout() call val() would return the entered value instead of the reseted value
						 */
						attribute.value = $('#'+attribute.inputID, $form).val();

						/*
						 * If the form is submited (non ajax) with errors, labels and input gets the class 'error'
						 */
						$('label,input',$form).each(function(){
							$(this).removeClass('error');
						});
					});
					$('#'+settings.summaryID+' ul').html('');
					$('#'+settings.summaryID).hide();
					//.. set to initial focus on reset
					if(settings.focus != undefined && !window.location.hash)
						$(settings.focus).focus();
				},1);
			});

			/*
			 * set to initial focus
			 */
			if(settings.focus != undefined && !window.location.hash)
				$(settings.focus).focus();
		});
	};

	/**
	 * Returns the container element of the specified attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param form the form jQuery object
	 * @return jquery the jquery representation of the container
	 */
	$.fn.yiiactiveform.getInputContainer = function(attribute, form) {
		if(attribute.inputContainer == undefined)
			return $('#'+attribute.inputID, form).closest('div');
		else
			return $(attribute.inputContainer).filter(':has("#'+attribute.inputID+'")');
	};

	/**
	 * updates the error message and the input container for a particular attribute.
	 * @param attribute object the configuration for a particular attribute.
	 * @param messages array the json data obtained from the ajax validation request
	 * @param form the form jQuery object
	 * @return boolean whether there is a validation error for the specified attribute
	 */
	$.fn.yiiactiveform.updateInput = function(attribute, messages, form) {
		attribute.status = 1;
		var hasError = messages!=null && $.isArray(messages[attribute.id]) && messages[attribute.id].length>0;
		var $error = $('#'+attribute.errorID, form);
		var $container = $.fn.yiiactiveform.getInputContainer(attribute, form);
		$container.removeClass(
			attribute.validatingCssClass + ' ' +
			attribute.errorCssClass + ' ' +
			attribute.successCssClass
		);

		if(hasError) {
			$error.html(messages[attribute.id][0]);
			$container.addClass(attribute.errorCssClass);
		}
		else if(attribute.enableAjaxValidation || attribute.clientValidation) {
			$container.addClass(attribute.successCssClass);
		}
		if(!attribute.hideErrorMessage)
			$error.toggle(hasError);

		attribute.value = $('#'+attribute.inputID, form).val();

		return hasError;
	};

	/**
	 * updates the error summary, if any.
	 * @param form jquery the jquery representation of the form
	 * @param messages array the json data obtained from the ajax validation request
	 */
	$.fn.yiiactiveform.updateSummary = function(form, messages) {
		var settings = $(form).data('settings');
		if (settings.summaryID == undefined)
			return;
		var content = '';
		$.each(settings.attributes, function(i, attribute){
			if(messages && $.isArray(messages[attribute.id])) {
				$.each(messages[attribute.id],function(j,message){
					content = content + '<li>' + message + '</li>';
				});
			}
		});
		$('#'+settings.summaryID+' ul').html(content);
		$('#'+settings.summaryID).toggle(content!='');
	};

	/**
	 * Performs the ajax validation request.
	 * This method is invoked internally to trigger the ajax validation.
	 * @param form jquery the jquery representation of the form
	 * @param successCallback function the function to be invoked if the ajax request succeeds
	 * @param errorCallback function the function to be invoked if the ajax request fails
	 */
	$.fn.yiiactiveform.validate = function(form, successCallback, errorCallback) {
		var $form = $(form);
		var settings = $form.data('settings');

		var messages = {};
		var needAjaxValidation = false;
		$.each(settings.attributes, function(){
			var msg = [];
			if (this.clientValidation != undefined && (settings.submitting || this.status == 2 || this.status == 3)) {
				var value = $('#'+this.inputID, $form).val();
				this.clientValidation(value, msg, this);
				if (msg.length) {
					messages[this.id] = msg;
				}
			}
			if (this.enableAjaxValidation && !msg.length && (settings.submitting || this.status == 2 || this.status == 3))
				needAjaxValidation = true;
		});

		if (!needAjaxValidation || settings.submitting && !$.isEmptyObject(messages)) {
			if(settings.submitting) {
				// delay callback so that the form can be submitted without problem
				setTimeout(function(){
					successCallback(messages);
				},200);
			}
			else {
				successCallback(messages);
			}
			return;
		}

		var $button = $form.data('submitObject');
		var extData = '&'+settings.ajaxVar+'='+$form.attr('id');
		if($button && $button.length)
			extData += '&'+$button.attr('name')+'='+$button.attr('value');

		$.ajax({
			url : settings.validationUrl,
			type : $form.attr('method'),
			data : $form.serialize()+extData,
			dataType : 'json',
			success : function(data) {
				if (data != null && typeof data == 'object') {
					$.each(settings.attributes, function() {
						if (!this.enableAjaxValidation)
							delete data[this.id];
					});
					successCallback($.extend({}, messages, data));
				}
				else {
					successCallback(messages);
				}
			},
			error : function() {
				if (errorCallback!=undefined) {
					errorCallback();
				}
			}
		});
	};

	/**
	 * Returns the configuration for the specified form.
	 * The configuration contains all needed information to perform ajax-based validation.
	 * @param form jquery the jquery representation of the form
	 * @return object the configuration for the specified form.
	 */
	$.fn.yiiactiveform.getSettings = function(form) {
		return $(form).data('settings');
	};

	$.fn.yiiactiveform.defaults = {
		ajaxVar: 'ajax',
		validationUrl: undefined,
		validationDelay: 200,
		validateOnSubmit : false,
		validateOnChange : true,
		validateOnType : false,
		hideErrorMessage : false,
		inputContainer : undefined,
		errorCssClass : 'error',
		successCssClass : 'success',
		validatingCssClass : 'validating',
		summaryID : undefined,
		timer: undefined,
		beforeValidateAttribute: undefined, // function(form, attribute) : boolean
		afterValidateAttribute: undefined,  // function(form, attribute, data, hasError)
		beforeValidate: undefined, // function(form) : boolean
		afterValidate: undefined,  // function(form, data, hasError) : boolean
		/**
		 * list of attributes to be validated. Each array element is of the following structure:
		 * {
		 *     id : 'ModelClass_attribute', // the unique attribute ID
		 *     model : 'ModelClass', // the model class name
		 *     name : 'name', // attribute name
		 *     inputID : 'input-tag-id',
		 *     errorID : 'error-tag-id',
		 *     value : undefined,
		 *     status : 0,  // 0: empty, not entered before,  1: validated, 2: pending validation, 3: validating
		 *     focus : undefined,  // jquery selector that indicates which element to receive input focus initially
		 *     validationDelay: 200,
		 *     validateOnChange : true,
		 *     validateOnType : false,
		 *     hideErrorMessage : false,
		 *     inputContainer : undefined,
		 *     errorCssClass : 'error',
		 *     successCssClass : 'success',
		 *     validatingCssClass : 'validating',
		 *     enableAjaxValidation : true,
		 *     enableClientValidation : true,
		 *     clientValidation : undefined, // function(value, messages, attribute) : client-side validation
		 *     beforeValidateAttribute: undefined, // function(form, attribute) : boolean
		 *     afterValidateAttribute: undefined,  // function(form, attribute, data, hasError)
		 * }
		 */
		attributes : []
	};

})(jQuery);<|endoftext|>


<commit_before>import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service';
import config from 'ghost-admin/config/environment';

export default Service.extend({
    ajax: service(),
    ghostPaths: service(),

    // This is needed so we can disable it in unit tests
    testing: undefined,

    scriptPromises: null,

    init() {
        this._super(...arguments);
        this.scriptPromises = {};

        if (this.testing === undefined) {
            this.testing = config.environment === 'test';
        }
    },

    loadScript(key, url) {
        if (this.testing) {
            return RSVP.resolve();
        }

        if (this.scriptPromises[key]) {
            return this.scriptPromises[key];
        }

        let scriptPromise = new RSVP.Promise((resolve, reject) => {
            let {adminRoot} = this.ghostPaths;

            let script = document.createElement('script');
            script.type = 'text/javascript';
            script.async = true;
            script.src = `${adminRoot}${url}`;

            let el = document.getElementsByTagName('script')[0];
            el.parentNode.insertBefore(script, el);

            script.addEventListener('load', () => {
                resolve();
            });

            script.addEventListener('error', () => {
                reject(new Error(`${url} failed to load`));
            });
        });

        this.scriptPromises[key] = scriptPromise;

        return scriptPromise;
    },

    loadStyle(key, url, alternate = false) {
        if (this.testing || document.querySelector(`#${key}-styles`)) {
            return RSVP.resolve();
        }

        return new RSVP.Promise((resolve, reject) => {
            let link = document.createElement('link');
            link.id = `${key}-styles`;
            link.rel = alternate ? 'alternate stylesheet' : 'stylesheet';
            link.href = `${this.ghostPaths.adminRoot}${url}`;
            link.onload = () => {
                if (alternate) {
                    // If stylesheet is alternate and we disable the stylesheet before injecting into the DOM,
                    // the onload handler never gets called. Thus, we should disable the link after it has finished loading
                    link.disabled = true;
                }
                resolve();
            };
            link.onerror = reject;

            if (alternate) {
                link.title = key;
            }

            document.querySelector('head').appendChild(link);
        });
    }
});
<commit_msg>🐛 Fixed night shift toggle in Firefox Nightly. (#1174)<commit_after>import RSVP from 'rsvp';
import Service, {inject as service} from '@ember/service';
import config from 'ghost-admin/config/environment';

export default Service.extend({
    ajax: service(),
    ghostPaths: service(),

    // This is needed so we can disable it in unit tests
    testing: undefined,

    scriptPromises: null,

    init() {
        this._super(...arguments);
        this.scriptPromises = {};

        if (this.testing === undefined) {
            this.testing = config.environment === 'test';
        }
    },

    loadScript(key, url) {
        if (this.testing) {
            return RSVP.resolve();
        }

        if (this.scriptPromises[key]) {
            return this.scriptPromises[key];
        }

        let scriptPromise = new RSVP.Promise((resolve, reject) => {
            let {adminRoot} = this.ghostPaths;

            let script = document.createElement('script');
            script.type = 'text/javascript';
            script.async = true;
            script.src = `${adminRoot}${url}`;

            let el = document.getElementsByTagName('script')[0];
            el.parentNode.insertBefore(script, el);

            script.addEventListener('load', () => {
                resolve();
            });

            script.addEventListener('error', () => {
                reject(new Error(`${url} failed to load`));
            });
        });

        this.scriptPromises[key] = scriptPromise;

        return scriptPromise;
    },

    loadStyle(key, url, alternate = false) {
        if (this.testing || document.querySelector(`#${key}-styles`)) {
            return RSVP.resolve();
        }

        return new RSVP.Promise((resolve, reject) => {
            let link = document.createElement('link');
            link.id = `${key}-styles`;
            link.rel = alternate ? 'alternate stylesheet' : 'stylesheet';
            link.href = `${this.ghostPaths.adminRoot}${url}`;
            link.onload = () => {
                link.onload = null;
                if (alternate) {
                    // If stylesheet is alternate and we disable the stylesheet before injecting into the DOM,
                    // the onload handler never gets called. Thus, we should disable the link after it has finished loading
                    link.disabled = true;
                }
                resolve();
            };
            link.onerror = reject;

            if (alternate) {
                link.title = key;
            }

            document.querySelector('head').appendChild(link);
        });
    }
});
<|endoftext|>


<commit_before>/* global forge */

forge["file"] = {
    /**
     * Allow the user to select an image and give a file object representing it.
     *
     * @param {Object} props
     * @param {function({uri: string, name: string})=} success
     * @param {function({message: string}=} error
     */
    "getImage": function (props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
            props = {};
        }
        if (!props) {
            props = {};
        }
        props.source = "gallery";
        forge.internal.call("file.getImage", props, success && function (uri) {
            var file = {
                uri: uri,
                name: "Image",
                type: "image"
            };
            if (props.width) {
                file.width = props.width;
            }
            if (props.height) {
                file.height = props.height;
            }
            success(file);
        }, error);
    },

    /**
     * Allow the user to select a video and give a file object representing it.
     *
     * @param {Object} props
     * @param {function({uri: string, name: string})=} success
     * @param {function({message: string}=} error
     */
    "getVideo": function (props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
            props = {};
        }
        if (!props) {
            props = {};
        }
        props.source = "gallery";
        forge.internal.call("file.getVideo", props, success && function (uri) {
            var file = {
                uri: uri,
                name: "Video",
                type: "video"
            };
            success(file);
        }, error);
    },

    /**
     * Get file object for a local file.
     *
     * @param {string} name
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "getLocal": function (path, success, error) {
        forge.internal.call("file.getLocal", {name: path}, success, error);
    },

    /**
     * Returns file information
     *
     * @param {{uri: string, name: string}} file
     * @param {function(object)=} success
     * @param {function({message: string}=} error
     */
    "info": function (file, success, error) {
        forge.internal.call("file.info", file, success, error);
    },

    /**
     * Get the base64 value for a files contents.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "base64": function (file, success, error) {
        forge.internal.call("file.base64", file, success, error);
    },

    /**
     * Get the string value for a files contents.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "string": function (file, success, error) {
        forge.internal.call("file.string", file, success, error);
    },

    /**
     * Get the URL an image which is no bigger than the given height and width.
     *
     * URL must be useable in the current scope of the code, may return a base64 data: URI.
     *
     * @param {{uri: string, name: string}} file
     * @param {Object} props
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "URL": function (file, props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
        }
        // Avoid mutating original file
        var newFile = {};
        for (var prop in file) {
            newFile[prop] = file[prop];
        }
        newFile.height = props.height || file.height || undefined;
        newFile.width = props.width || file.width || undefined;
        if (forge.httpd && forge.config.modules.httpd.config.port) {
            forge.internal.call("file.URL", newFile, function (url) {
                success(forge.httpd.normalize(url));
            }, error);
        } else {
            forge.internal.call("file.URL", newFile, success, error);
        }
    },

    /**
     * Check a file object represents a file which exists.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(boolean)=} success
     * @param {function({message: string}=} error
     */
    "isFile": function (file, success, error) {
        if (!file || !("uri" in file)) {
            success(false);
        } else {
            forge.internal.call("file.isFile", file, success, error);
        }
    },

    /**
     * Download and save a URL, return the file object representing saved file.
     *
     * @param {string} URL
     * @param {function({uri: string})=} success
     * @param {function({message: string}=} error
     */
    "cacheURL": function (url, success, error) {
        forge.internal.call("file.cacheURL", { url: url }, success && function (uri) {
            success({
                uri: uri
            });
        }, error);
    },
    "saveURL": function (url, success, error) {
        forge.internal.call("file.saveURL", { url: url }, success && function (uri) {
            success({
                uri: uri
            });
        }, error);
    },

    /**
     * Delete a file.
     *
     * @param {{uri: string} file
     * @param {function()=} success
     * @param {function({message: string}=} error
     */
    "remove": function (file, success, error) {
        forge.internal.call("file.remove", file, success, error);
    },

    /**
     * Delete all cached files
     *
     * @param {function()=} success
     * @param {function({message: string}=} error
     */
    "clearCache": function (success, error) {
        forge.internal.call("file.clearCache", {}, success, error);
    },

    /**
     * Retrieve device storage information
     *
     * @param {function()=} success
     * @param {function({total: number, free: number, app: number, cache: number})=} success
     */
    "getStorageInformation": function (success, error) {
        forge.internal.call("file.getStorageInformation", {}, success, error);
    }
};
<commit_msg>Support build-in webserver<commit_after>/* global forge */

forge["file"] = {
    /**
     * Allow the user to select an image and give a file object representing it.
     *
     * @param {Object} props
     * @param {function({uri: string, name: string})=} success
     * @param {function({message: string}=} error
     */
    "getImage": function (props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
            props = {};
        }
        if (!props) {
            props = {};
        }
        props.source = "gallery";
        forge.internal.call("file.getImage", props, success && function (uri) {
            var file = {
                uri: uri,
                name: "Image",
                type: "image"
            };
            if (props.width) {
                file.width = props.width;
            }
            if (props.height) {
                file.height = props.height;
            }
            success(file);
        }, error);
    },

    /**
     * Allow the user to select a video and give a file object representing it.
     *
     * @param {Object} props
     * @param {function({uri: string, name: string})=} success
     * @param {function({message: string}=} error
     */
    "getVideo": function (props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
            props = {};
        }
        if (!props) {
            props = {};
        }
        props.source = "gallery";
        forge.internal.call("file.getVideo", props, success && function (uri) {
            var file = {
                uri: uri,
                name: "Video",
                type: "video"
            };
            success(file);
        }, error);
    },

    /**
     * Get file object for a local file.
     *
     * @param {string} name
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "getLocal": function (path, success, error) {
        forge.internal.call("file.getLocal", {name: path}, success, error);
    },

    /**
     * Returns file information
     *
     * @param {{uri: string, name: string}} file
     * @param {function(object)=} success
     * @param {function({message: string}=} error
     */
    "info": function (file, success, error) {
        forge.internal.call("file.info", file, success, error);
    },

    /**
     * Get the base64 value for a files contents.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "base64": function (file, success, error) {
        forge.internal.call("file.base64", file, success, error);
    },

    /**
     * Get the string value for a files contents.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "string": function (file, success, error) {
        forge.internal.call("file.string", file, success, error);
    },

    /**
     * Get the URL an image which is no bigger than the given height and width.
     *
     * URL must be useable in the current scope of the code, may return a base64 data: URI.
     *
     * @param {{uri: string, name: string}} file
     * @param {Object} props
     * @param {function(string)=} success
     * @param {function({message: string}=} error
     */
    "URL": function (file, props, success, error) {
        if (typeof props === "function") {
            error = success;
            success = props;
        }
        // Avoid mutating original file
        var newFile = {};
        for (var prop in file) {
            newFile[prop] = file[prop];
        }
        newFile.height = props.height || file.height || undefined;
        newFile.width = props.width || file.width || undefined;
        if (forge.flags.android_disable_httpd === true || forge.flags.ios_disable_httpd === true) {
            forge.internal.call("file.URL", newFile, success, error);
        } else {
            forge.internal.call("file.URL", newFile, function (url) {
                success(forge.httpd.normalize(url));
            }, error);
        }
    },

    /**
     * Check a file object represents a file which exists.
     *
     * @param {{uri: string, name: string}} file
     * @param {function(boolean)=} success
     * @param {function({message: string}=} error
     */
    "isFile": function (file, success, error) {
        if (!file || !("uri" in file)) {
            success(false);
        } else {
            forge.internal.call("file.isFile", file, success, error);
        }
    },

    /**
     * Download and save a URL, return the file object representing saved file.
     *
     * @param {string} URL
     * @param {function({uri: string})=} success
     * @param {function({message: string}=} error
     */
    "cacheURL": function (url, success, error) {
        forge.internal.call("file.cacheURL", { url: url }, success && function (uri) {
            success({
                uri: uri
            });
        }, error);
    },
    "saveURL": function (url, success, error) {
        forge.internal.call("file.saveURL", { url: url }, success && function (uri) {
            success({
                uri: uri
            });
        }, error);
    },

    /**
     * Delete a file.
     *
     * @param {{uri: string} file
     * @param {function()=} success
     * @param {function({message: string}=} error
     */
    "remove": function (file, success, error) {
        forge.internal.call("file.remove", file, success, error);
    },

    /**
     * Delete all cached files
     *
     * @param {function()=} success
     * @param {function({message: string}=} error
     */
    "clearCache": function (success, error) {
        forge.internal.call("file.clearCache", {}, success, error);
    },

    /**
     * Retrieve device storage information
     *
     * @param {function()=} success
     * @param {function({total: number, free: number, app: number, cache: number})=} success
     */
    "getStorageInformation": function (success, error) {
        forge.internal.call("file.getStorageInformation", {}, success, error);
    }
};
<|endoftext|>


<commit_before>/**
 * @class CM_FormField_File
 * @extends CM_FormField_Abstract
 */
var CM_FormField_File = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_File',

  events: {
    'click .deleteFile': function(e) {
      $(e.currentTarget).closest('.preview').remove();
    }
  },

  ready: function() {
    var field = this;
    var $input = this.getInput();
    var allowedExtensions = field.getOption("allowedExtensions");
    var allowedExtensionsRegexp = _.isEmpty(allowedExtensions) ? null : new RegExp('\.(' + allowedExtensions.join('|') + ')$', 'i');
    var inProgressCount = 0;

    if (!Modernizr.fileinput) {
      $input.prop('disabled', true);
      field.$('.uploadButton').addClass('disabled');
      field.$('.notSupported').show();
    }

    // remove attr multiple on iPhone, iPod, iPad to allow upload photos via camera
    if (navigator.userAgent.match(/iP(ad|hone|od)/i)) {
      $input.removeAttr('multiple');
    }

    $input.fileupload({
      dataType: 'json',
      url: cm.getUrl('/upload', {'field': field.getClass()}, true),
      dropZone: this.$el,
      acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
      formData: function(form) {
        return $input;
      },
      send: function(e, data) {
        inProgressCount++;
        field.error(null);
        _.each(data.files, function(file) {
          if (allowedExtensionsRegexp && !allowedExtensionsRegexp.test(file.name)) {
            field.error(cm.language.get('{$file} has an invalid extension. Only {$extensions} are allowed.', {
              file: file.name,
              extensions: allowedExtensions.join(', ')
            }));
            file.error = true;
          }
        });
        data.files = _.reject(data.files, function(file) {
          return file.error;
        });
        if (_.isEmpty(data.files)) {
          data.skipFailMessage = true;
          return false;
        }
        data.$preview = $('

');
        field.$('.previews').append(data.$preview);

        field.$('.uploadButton').attr('data-progress', '');

      },
      done: function(e, data) {
        inProgressCount--;
        if (data.result.success) {
          data.$preview.html(data.result.success.preview + '<input type="hidden" name="' + field.getName() + '[]" value="' + data.result.success.id + '"/>');
        } else if (data.result.error) {
          data.$preview.remove();
          field.error(data.result.error.msg);
        }
        if (inProgressCount === 0) {
          field.trigger("uploadComplete", data.files);
        }
      },
      fail: function(e, data) {
        inProgressCount--;
        if (data.$preview) {
          data.$preview.remove();
        }
        if (!data.skipFailMessage) {
          field.error('Upload error');
        }
      },
      always: function() {
        if (inProgressCount === 0) {
          field.$('.uploadButton').removeAttr('data-progress');
        }
      }
    });

    this.bindJquery($(document), 'dragenter', function() {
      field.$el.addClass('dragover');
    });
    this.bindJquery($(document), 'drop', function() {
      field.$el.removeClass('dragover');
    });
    this.bindJquery($(document), 'drop dragover', function(e) {
      e.preventDefault();
    });
  },

  getInput: function() {
    return this.$('input[type="file"]');
  },

  getValue: function() {
    var value = this.$('input[name="' + this.options.params.name + '[]"]').map(function() {
      return $(this).val();
    }).get();
    value = _.compact(value);
    return value;
  },

  setValue: function(value) {
    throw new CM_Exception('Not implemented');
  },

  reset: function() {
    this.$('.previews').empty();
  }
});
<commit_msg>Bring back cardinality check to CM_FormField_File<commit_after>/**
 * @class CM_FormField_File
 * @extends CM_FormField_Abstract
 */
var CM_FormField_File = CM_FormField_Abstract.extend({
  _class: 'CM_FormField_File',

  events: {
    'click .deleteFile': function(e) {
      $(e.currentTarget).closest('.preview').remove();
    }
  },

  ready: function() {
    var field = this;
    var $input = this.getInput();
    var allowedExtensions = field.getOption("allowedExtensions");
    var allowedExtensionsRegexp = _.isEmpty(allowedExtensions) ? null : new RegExp('\.(' + allowedExtensions.join('|') + ')$', 'i');
    var inProgressCount = 0;

    if (!Modernizr.fileinput) {
      $input.prop('disabled', true);
      field.$('.uploadButton').addClass('disabled');
      field.$('.notSupported').show();
    }

    // remove attr multiple on iPhone, iPod, iPad to allow upload photos via camera
    if (navigator.userAgent.match(/iP(ad|hone|od)/i)) {
      $input.removeAttr('multiple');
    }

    $input.fileupload({
      dataType: 'json',
      url: cm.getUrl('/upload', {'field': field.getClass()}, true),
      dropZone: this.$el,
      acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
      formData: function(form) {
        return $input;
      },
      send: function(e, data) {
        inProgressCount++;
        field.error(null);
        _.each(data.files, function(file) {
          if (allowedExtensionsRegexp && !allowedExtensionsRegexp.test(file.name)) {
            field.error(cm.language.get('{$file} has an invalid extension. Only {$extensions} are allowed.', {
              file: file.name,
              extensions: allowedExtensions.join(', ')
            }));
            file.error = true;
          }
        });
        data.files = _.reject(data.files, function(file) {
          return file.error;
        });
        if (_.isEmpty(data.files)) {
          data.skipFailMessage = true;
          return false;
        }
        data.$preview = $('

');
        field.$('.previews').append(data.$preview);

        field.$('.uploadButton').attr('data-progress', '');

      },
      done: function(e, data) {
        inProgressCount--;
        if (data.result.success) {
          data.$preview.html(data.result.success.preview + '<input type="hidden" name="' + field.getName() + '[]" value="' + data.result.success.id + '"/>');
        } else if (data.result.error) {
          data.$preview.remove();
          field.error(data.result.error.msg);
        }
        if (inProgressCount === 0) {
          var cardinality = field.getOption("cardinality");
          if (cardinality > 0) {
            var $previews = field.$('.previews .preview');
            if ($previews.length > cardinality) {
              $previews.slice(0, $previews.length - cardinality).remove();
            }
          }
          field.trigger("uploadComplete", data.files);
        }
      },
      fail: function(e, data) {
        inProgressCount--;
        if (data.$preview) {
          data.$preview.remove();
        }
        if (!data.skipFailMessage) {
          field.error('Upload error');
        }
      },
      always: function() {
        if (inProgressCount === 0) {
          field.$('.uploadButton').removeAttr('data-progress');
        }
      }
    });

    this.bindJquery($(document), 'dragenter', function() {
      field.$el.addClass('dragover');
    });
    this.bindJquery($(document), 'drop', function() {
      field.$el.removeClass('dragover');
    });
    this.bindJquery($(document), 'drop dragover', function(e) {
      e.preventDefault();
    });
  },

  getInput: function() {
    return this.$('input[type="file"]');
  },

  getValue: function() {
    var value = this.$('input[name="' + this.options.params.name + '[]"]').map(function() {
      return $(this).val();
    }).get();
    value = _.compact(value);
    return value;
  },

  setValue: function(value) {
    throw new CM_Exception('Not implemented');
  },

  reset: function() {
    this.$('.previews').empty();
  }
});
<|endoftext|>


<commit_before>import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import SearchHit from "../../components/SearchHit/SearchHit";
import SearchHitCategory
  from "../../components/SearchHitCategory/SearchHitCategory";
import { Container, Col } from "reactstrap";
import FloatingActionButton from 'material-ui/FloatingActionButton'
import Plus from 'material-ui/svg-icons/content/add'
import "./SearchResultsView.css";
import { injectIntl, intlShape } from "react-intl";
import preventDefault from "react-prevent-default";
import Chip from "material-ui/Chip";
import myhistory from "../../utils/history";
import queryString from "query-string";
import searchGridIcon from "../../img/pp-search-grid-icon.png";
import searchGridIconActive from "../../img/pp-search-grid-icon-active.png";
import searchListIcon from "../../img/pp-search-list-icon.png";
import searchListIconActive from "../../img/pp-search-list-icon-active.png";
import { ChasingDots } from "better-react-spinkit";

class LinkToSearch extends React.Component {
  render() {
    let { label, query, intl } = this.props;
    return (

        <Link to={`/search?${queryString.stringify(query)}`}>
          {intl.formatMessage({ id: label })}
        </Link>

    );
  }
}

class FeaturedSearches extends React.Component {
  render() {
    return (

        <LinkToSearch
          label="mention_participatory_budgeting"
          query={{ query: "participatory budgeting" }}
          intl={this.props.intl}
        />
        <LinkToSearch
          label="tag_infrastructure"
          query={{ tag: "infrastructure" }}
          intl={this.props.intl}
        />

    );
  }
}

class FilterArray extends React.Component {
  constructor(props) {
    super(props);
    this.state = { chipData: props.data };
    this.styles = {
      chip: {
        margin: 4
      },
      wrapper: {
        display: "flex",
        flexWrap: "wrap"
      }
    };
  }

  handleRequestDelete = key => {
    let parameters = queryString.parse(myhistory.location.search);
    delete parameters[key];
    let newquerystring = queryString.stringify(parameters);
    myhistory.push(`/search?${newquerystring}`);
  };

  renderChip(data) {
    return (
      <Chip
        key={data.key}
        onRequestDelete={() => this.handleRequestDelete(data.key)}
        style={this.styles.chip}
      >
        {data.label}
      </Chip>
    );
  }

  render() {
    return (

        {this.state.chipData.map(this.renderChip, this)}

    );
  }
}

export class SearchResultsView extends React.Component {
  constructor() {
    super();
    this.state = { value: "All" };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({ value: event.target.value });
    this.props.onCategoryChange.bind(this, event.target.value)();
  }

  render() {
    let data = this.props.data;

    let selectedViewType = this.props.selectedViewType;
    let searchresults = data.map(function(result, index) {
      return (
        <SearchHit
          selectedViewType={selectedViewType}
          key={index}
          record={result}
        />
      );
    });
    let formatMessage = this.props.intl.formatMessage;
    let addLink = "/quick-submit"
    let resultsCount = data.length;
    let { searching, query } = this.props;
    let results = "";
    if (this.props.searching) {
      results = (

            {formatMessage({ id: "searching_for" })}

            {query}
            {
<ChasingDots size={50} />
}

      );
    } else {
      let description = `Searched for:`;
      if (searching) {
        description = "Searching for:";
      }
      let restrictions = queryString.parse(myhistory.location.search);
      let filters = [];
      let searchTerm = "";
      Object.keys(restrictions).forEach(function(key, index) {
        if (key === "query") {
          searchTerm = restrictions[key];
        } else {
          filters.push({
            key: key,
            label: formatMessage({ id: key }) + " : " + restrictions[key]
          });
        }
      });

      results = (

            {searchTerm
              ?

                  {description}
{searchTerm}

              :
}
            <FilterArray data={filters} />

              {resultsCount}
              {this.props.intl.formatMessage({
                id: "result" + (resultsCount === 1 ? "" : "s")
              })}

              {searchresults}

      );
    }
    return (

        <Container className="search-results-component" fluid={true}>
          <Col md="3" className="sidepanel hidden-sm-down">

 {
                  this.setState({
                    sortingSelectionOpen: !this.state.sortingSelectionOpen
                  });
                }}
              >
                {this.props.intl.formatMessage({
                  id: this.props.sortingMethod
                })}

                {this.props.intl.formatMessage({ id: "featured" })}

                {this.props.intl.formatMessage({ id: "chronological" })}

                {this.props.intl.formatMessage({ id: "alphabetical" })}

                {this.props.intl.formatMessage({ id: "featured_searches" })}

                <FeaturedSearches intl={this.props.intl} />

          </Col>
          <Col md="9" className="results-area">

                    preventDefault(this.props.onCategoryChange("All"))}
                  className={
                    this.props.selectedCategory === "All"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "all" })}

                    preventDefault(this.props.onCategoryChange("News"))}
                  className={
                    this.props.selectedCategory === "News"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "news" })}

                    preventDefault(this.props.onCategoryChange("Cases"))}
                  className={
                    this.props.selectedCategory === "Cases"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "cases" })}

                    preventDefault(this.props.onCategoryChange("Methods"))}
                  className={
                    this.props.selectedCategory === "Methods"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "methods" })}

                    preventDefault(
                      this.props.onCategoryChange("Organizations")
                    )}
                  className={
                    this.props.selectedCategory === "Organizations"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "organizations" })}

              <select
                className="mobile-select hidden-sm-up"
                value={this.state.value}
                onChange={this.handleChange}
              >
                <option value="All">
                  {this.props.intl.formatMessage({ id: "all" })}
                </option>
                <option value="News">
                  {this.props.intl.formatMessage({ id: "news" })}
                </option>
                <option value="Cases">
                  {this.props.intl.formatMessage({ id: "cases" })}
                </option>
                <option value="Methods">
                  {this.props.intl.formatMessage({ id: "methods" })}
                </option>
                <option value="Organizations">
                  {this.props.intl.formatMessage({ id: "organizations" })}
                </option>
              </select>

                    preventDefault(this.props.onLayoutChange("grid"))}
                  className={
                    this.props.selectedViewType === "grid"
                      ? "selected"
                      : "unselected"
                  }
                >

                    preventDefault(this.props.onLayoutChange("list"))}
                  className={
                    this.props.selectedViewType === "list"
                      ? "selected"
                      : "unselected"
                  }
                >

            {results}
          </Col>
        </Container>
        <Link to={addLink}>
         <FloatingActionButton className='hidden-sm-up editButton'>
          <Plus />
         </FloatingActionButton>
        </Link>

    );
  }
}

SearchResultsView.propTypes = {
  selectedCategory: PropTypes.string.isRequired,
  sortingMethod: PropTypes.string.isRequired,
  query: PropTypes.string.isRequired,
  intl: intlShape.isRequired
};

let config = { allowMultiple: true };

export default injectIntl(SearchResultsView, config);
<commit_msg>linting fixes<commit_after>import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import SearchHit from "../../components/SearchHit/SearchHit";
import { Container, Col } from "reactstrap";
import FloatingActionButton from "material-ui/FloatingActionButton";
import Plus from "material-ui/svg-icons/content/add";
import "./SearchResultsView.css";
import { injectIntl, intlShape } from "react-intl";
import preventDefault from "react-prevent-default";
import Chip from "material-ui/Chip";
import myhistory from "../../utils/history";
import queryString from "query-string";
import searchGridIcon from "../../img/pp-search-grid-icon.png";
import searchGridIconActive from "../../img/pp-search-grid-icon-active.png";
import searchListIcon from "../../img/pp-search-list-icon.png";
import searchListIconActive from "../../img/pp-search-list-icon-active.png";
import { ChasingDots } from "better-react-spinkit";

class LinkToSearch extends React.Component {
  render() {
    let { label, query, intl } = this.props;
    return (

        <Link to={`/search?${queryString.stringify(query)}`}>
          {intl.formatMessage({ id: label })}
        </Link>

    );
  }
}

class FeaturedSearches extends React.Component {
  render() {
    return (

        <LinkToSearch
          label="mention_participatory_budgeting"
          query={{ query: "participatory budgeting" }}
          intl={this.props.intl}
        />
        <LinkToSearch
          label="tag_infrastructure"
          query={{ tag: "infrastructure" }}
          intl={this.props.intl}
        />

    );
  }
}

class FilterArray extends React.Component {
  constructor(props) {
    super(props);
    this.state = { chipData: props.data };
    this.styles = {
      chip: {
        margin: 4
      },
      wrapper: {
        display: "flex",
        flexWrap: "wrap"
      }
    };
  }

  handleRequestDelete = key => {
    let parameters = queryString.parse(myhistory.location.search);
    delete parameters[key];
    let newquerystring = queryString.stringify(parameters);
    myhistory.push(`/search?${newquerystring}`);
  };

  renderChip(data) {
    return (
      <Chip
        key={data.key}
        onRequestDelete={() => this.handleRequestDelete(data.key)}
        style={this.styles.chip}
      >
        {data.label}
      </Chip>
    );
  }

  render() {
    return (

        {this.state.chipData.map(this.renderChip, this)}

    );
  }
}

export class SearchResultsView extends React.Component {
  constructor() {
    super();
    this.state = { value: "All" };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({ value: event.target.value });
    this.props.onCategoryChange.bind(this, event.target.value)();
  }

  render() {
    let data = this.props.data;

    let selectedViewType = this.props.selectedViewType;
    let searchresults = data.map(function(result, index) {
      return (
        <SearchHit
          selectedViewType={selectedViewType}
          key={index}
          record={result}
        />
      );
    });
    let formatMessage = this.props.intl.formatMessage;
    let addLink = "/quick-submit";
    let resultsCount = data.length;
    let { searching, query } = this.props;
    let results = "";
    if (this.props.searching) {
      results = (

            {formatMessage({ id: "searching_for" })}

            {query}
            {
<ChasingDots size={50} />
}

      );
    } else {
      let description = `Searched for:`;
      if (searching) {
        description = "Searching for:";
      }
      let restrictions = queryString.parse(myhistory.location.search);
      let filters = [];
      let searchTerm = "";
      Object.keys(restrictions).forEach(function(key, index) {
        if (key === "query") {
          searchTerm = restrictions[key];
        } else {
          filters.push({
            key: key,
            label: formatMessage({ id: key }) + " : " + restrictions[key]
          });
        }
      });

      results = (

            {searchTerm
              ?

                  {description}
{searchTerm}

              :
}
            <FilterArray data={filters} />

              {resultsCount}
              {this.props.intl.formatMessage({
                id: "result" + (resultsCount === 1 ? "" : "s")
              })}

              {searchresults}

      );
    }
    return (

        <Container className="search-results-component" fluid={true}>
          <Col md="3" className="sidepanel hidden-sm-down">

 {
                  this.setState({
                    sortingSelectionOpen: !this.state.sortingSelectionOpen
                  });
                }}
              >
                {this.props.intl.formatMessage({
                  id: this.props.sortingMethod
                })}

                {this.props.intl.formatMessage({ id: "featured" })}

                {this.props.intl.formatMessage({ id: "chronological" })}

                {this.props.intl.formatMessage({ id: "alphabetical" })}

                {this.props.intl.formatMessage({ id: "featured_searches" })}

                <FeaturedSearches intl={this.props.intl} />

          </Col>
          <Col md="9" className="results-area">

                    preventDefault(this.props.onCategoryChange("All"))}
                  className={
                    this.props.selectedCategory === "All"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "all" })}

                    preventDefault(this.props.onCategoryChange("News"))}
                  className={
                    this.props.selectedCategory === "News"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "news" })}

                    preventDefault(this.props.onCategoryChange("Cases"))}
                  className={
                    this.props.selectedCategory === "Cases"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "cases" })}

                    preventDefault(this.props.onCategoryChange("Methods"))}
                  className={
                    this.props.selectedCategory === "Methods"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "methods" })}

                    preventDefault(
                      this.props.onCategoryChange("Organizations")
                    )}
                  className={
                    this.props.selectedCategory === "Organizations"
                      ? "selected"
                      : "unselected"
                  }
                >
                  {this.props.intl.formatMessage({ id: "organizations" })}

              <select
                className="mobile-select hidden-sm-up"
                value={this.state.value}
                onChange={this.handleChange}
              >
                <option value="All">
                  {this.props.intl.formatMessage({ id: "all" })}
                </option>
                <option value="News">
                  {this.props.intl.formatMessage({ id: "news" })}
                </option>
                <option value="Cases">
                  {this.props.intl.formatMessage({ id: "cases" })}
                </option>
                <option value="Methods">
                  {this.props.intl.formatMessage({ id: "methods" })}
                </option>
                <option value="Organizations">
                  {this.props.intl.formatMessage({ id: "organizations" })}
                </option>
              </select>

                    preventDefault(this.props.onLayoutChange("grid"))}
                  className={
                    this.props.selectedViewType === "grid"
                      ? "selected"
                      : "unselected"
                  }
                >

                    preventDefault(this.props.onLayoutChange("list"))}
                  className={
                    this.props.selectedViewType === "list"
                      ? "selected"
                      : "unselected"
                  }
                >

            {results}
          </Col>
        </Container>
        <Link to={addLink}>
          <FloatingActionButton className="hidden-sm-up editButton">
            <Plus />
          </FloatingActionButton>
        </Link>

    );
  }
}

SearchResultsView.propTypes = {
  selectedCategory: PropTypes.string.isRequired,
  sortingMethod: PropTypes.string.isRequired,
  query: PropTypes.string.isRequired,
  intl: intlShape.isRequired
};

let config = { allowMultiple: true };

export default injectIntl(SearchResultsView, config);
<|endoftext|>