<commit_before>(function( jQuery ) {

var rclass = /[\n\t\r]/g,
	rspaces = /\s+/,
	rreturn = /\r/g,
	rtype = /^(?:button|input)$/i,
	rfocusable = /^(?:button|input|object|select|textarea)$/i,
	rclickable = /^a(?:rea)?$/i,
	rradiocheck = /^(?:radio|checkbox)$/i,
	formHook;

jQuery.fn.extend({
	attr: function( name, value ) {
		return jQuery.access( this, name, value, true, jQuery.attr );
	},

	removeAttr: function( name ) {
		return this.each(function() {
			if ( this.nodeType === 1 ) {
				jQuery.removeAttr( this, name );
			}
		});
	},

	prop: function( name, value ) {
		return jQuery.access( this, name, value, true, jQuery.prop );
	},

	removeProp: function( name ) {
		return this.each(function() {
			// try/catch handles cases where IE balks (such as removing a property on window)
			try {
				this[ name ] = undefined;
				delete this[ name ];
			} catch( e ) {}
		});
	},

	addClass: function( value ) {
		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.addClass( value.call(this, i, self.attr("class") || "") );
			});
		}

		if ( value && typeof value === "string" ) {
			var classNames = (value || "").split( rspaces );

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 ) {
					if ( !elem.className ) {
						elem.className = value;

					} else {
						var className = " " + elem.className + " ",
							setClass = elem.className;

						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
								setClass += " " + classNames[c];
							}
						}
						elem.className = jQuery.trim( setClass );
					}
				}
			}
		}

		return this;
	},

	removeClass: function( value ) {
		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.removeClass( value.call(this, i, self.attr("class")) );
			});
		}

		if ( (value && typeof value === "string") || value === undefined ) {
			var classNames = (value || "").split( rspaces );

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 && elem.className ) {
					if ( value ) {
						var className = (" " + elem.className + " ").replace(rclass, " ");
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							className = className.replace(" " + classNames[c] + " ", " ");
						}
						elem.className = jQuery.trim( className );

					} else {
						elem.className = "";
					}
				}
			}
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var type = typeof value,
			isBool = typeof stateVal === "boolean";

		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
			});
		}

		return this.each(function() {
			if ( type === "string" ) {
				// toggle individual class names
				var className,
					i = 0,
					self = jQuery( this ),
					state = stateVal,
					classNames = value.split( rspaces );

				while ( (className = classNames[ i++ ]) ) {
					// check each className given, space seperated list
					state = isBool ? state : !self.hasClass( className );
					self[ state ? "addClass" : "removeClass" ]( className );
				}

			} else if ( type === "undefined" || type === "boolean" ) {
				if ( this.className ) {
					// store className if set
					jQuery._data( this, "__className__", this.className );
				}

				// toggle whole className
				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
			}
		});
	},

	hasClass: function( selector ) {
		var className = " " + selector + " ";
		for ( var i = 0, l = this.length; i < l; i++ ) {
			if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
				return true;
			}
		}

		return false;
	},

	val: function( value ) {
		if ( !arguments.length ) {
			var elem = this[0];

			if ( elem ) {
				if ( jQuery.nodeName( elem, "option" ) ) {
					// attributes.value is undefined in Blackberry 4.7 but
					// uses .value. See #6932
					var val = elem.attributes.value;
					return !val || val.specified ? elem.value : elem.text;
				}

				// We need to handle select boxes special
				if ( jQuery.nodeName( elem, "select" ) ) {
					var index = elem.selectedIndex,
						values = [],
						options = elem.options,
						one = elem.type === "select-one";

					// Nothing was selected
					if ( index < 0 ) {
						return null;
					}

					// Loop through all the selected options
					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
						var option = options[ i ];

						// Don't return options that are disabled or in a disabled optgroup
						if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
								(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {

							// Get the specific value for the option
							value = jQuery(option).val();

							// We don't need an array for one selects
							if ( one ) {
								return value;
							}

							// Multi-Selects return an array
							values.push( value );
						}
					}

					// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
					if ( one && !values.length && options.length ) {
						return jQuery( options[ index ] ).val();
					}

					return values;
				}

				// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
				if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
					return elem.getAttribute("value") === null ? "on" : elem.value;
				}

				// Everything else, we just grab the value
				return (elem.value || "").replace(rreturn, "");

			}

			return undefined;
		}

		var isFunction = jQuery.isFunction(value);

		return this.each(function(i) {
			var self = jQuery(this), val = value;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( isFunction ) {
				val = value.call(this, i, self.val());
			}

			// Treat null/undefined as ""; convert numbers to string
			if ( val == null ) {
				val = "";
			} else if ( typeof val === "number" ) {
				val += "";
			} else if ( jQuery.isArray(val) ) {
				val = jQuery.map(val, function (value) {
					return value == null ? "" : value + "";
				});
			}

			if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
				this.checked = jQuery.inArray( self.val(), val ) >= 0;

			} else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(val);

				jQuery( "option", this ).each(function() {
					this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
				});

				if ( !values.length ) {
					this.selectedIndex = -1;
				}

			} else {
				this.value = val;
			}
		});
	}
});

jQuery.extend({
	attrFn: {
		val: true,
		css: true,
		html: true,
		text: true,
		data: true,
		width: true,
		height: true,
		offset: true
	},

	attrFix: {
		// Always normalize to ensure hook usage
		tabindex: "tabIndex"
	},

	attr: function( elem, name, value, pass ) {
		var nType = elem.nodeType;

		// don't get/set attributes on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return undefined;
		}

		if ( pass && name in jQuery.attrFn ) {
			return jQuery(elem)[name](value);
		}

		var ret, hooks,
			notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

		// Normalize the name if needed
		name = notxml && jQuery.attrFix[ name ] || name;

		// Get the appropriate hook, or the formHook if getSetAttribute is not supported and we have form objects in IE6/7
		hooks = formHook && ( name === "name" || elem.nodeName === "FORM" ) ?
					formHook( name ) :
					jQuery.attrHooks[ name ];

		if ( value !== undefined ) {

			if ( value === null ) {
				jQuery.removeAttr( elem, name );
				return undefined;

			} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value )) !== undefined ) {
				return ret;

			} else {
				elem.setAttribute( name, value );
				return value;
			}

		} else {

			if ( hooks && "get" in hooks && notxml ) {
				return hooks.get( elem );

			} else {

				ret = elem.getAttribute( name );

				// Non-existent attributes return null, we normalize to undefined
				return ret === null ?
					undefined :
					ret;
			}
		}
	},

	removeAttr: function( elem, name ) {
		name = jQuery.attrFix[ name ] || name;

		if ( jQuery.support.getSetAttribute ) {
			elem.removeAttribute( name );
		} else {
			// Set to default empty string
			elem.setAttribute( name, "" );
			// Attempt to remove completely with DOM level 1
			elem.removeAttributeNode( elem.getAttributeNode( name ) );
		}
	},

	attrHooks: {
		type: {
			set: function( elem, value ) {
				// We can't allow the type property to be changed (since it causes problems in IE)
				if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
					jQuery.error( "type property can't be changed" );
				}
			}
		},
		tabIndex: {
			get: function( elem ) {
				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				var attributeNode = elem.getAttributeNode("tabIndex");

				return attributeNode && attributeNode.specified ?
					parseInt( attributeNode.value, 10 ) :
					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
						0 :
						undefined;
			}
		}
	},

	propFix: {},

	prop: function( elem, name, value ) {
		var nType = elem.nodeType;

		// don't get/set properties on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return undefined;
		}

		var ret, hooks,
			notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

		// Try to normalize/fix the name
		name = notxml && jQuery.propFix[ name ] || name;

		hooks = jQuery.propHooks[ name ];

		if ( value !== undefined ) {
			if ( hooks && "set" in hooks && (ret = hooks.set( elem, value )) !== undefined ) {
				return ret;

			} else {
				return (elem[ name ] = value);
			}

		} else {
			if ( hooks && "get" in hooks && (ret = hooks.get( elem )) !== undefined ) {
				return ret;

			} else {
				return elem[ name ];
			}
		}
	},

	propHooks: {}
});

// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !jQuery.support.getSetAttribute ) {
	jQuery.attrFix = jQuery.extend( jQuery.attrFix, {
		"for": "htmlFor",
		"class": "className",
		readonly: "readOnly",
		maxlength: "maxLength",
		cellspacing: "cellSpacing",
		rowspan: "rowSpan",
		colspan: "colSpan",
		usemap: "useMap",
		frameborder: "frameBorder"
	});

	// Use this for any attribute on a form in IE6/7
	// And the name attribute
	formHook = function( name ) {
		return jQuery.attrHooks[ name ] || {
			get: function( elem ) {
				var ret = elem.getAttributeNode( name );
				// Return undefined if not specified instead of empty string
				return ret && ret.specified ?
					ret.nodeValue :
					undefined;
			},
			set: function( elem, value ) {
				// Check form objects in IE (multiple bugs related)
				// Only use nodeValue if the attribute node exists on the form
				var ret = elem.getAttributeNode( name );
				if ( ret ) {
					ret.nodeValue = value;
				} else {
					elem.setAttribute( name, value );
				}
				return value;
			}
		};
	};
}

// Remove certain attrs if set to false
jQuery.each([ "selected", "checked", "readonly", "disabled" ], function( i, name ) {
	name = jQuery.attrFix[ name ] || name;

	jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
		set: function( elem, value ) {
			if ( !value ) { // '', undefined, false, null will remove attr
				jQuery.removeAttr( elem, name );
				return false;
			}

			elem.setAttribute( name, value );
			return value;
		}
	});
});

// Some attributes require a special call on IE
if ( !jQuery.support.hrefNormalized ) {
	jQuery.each([ "href", "src", "style" ], function( i, name ) {
		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
			get: function( elem ) {
				return elem.getAttribute( name, 2 );
			}
		});
	});
}

if ( !jQuery.support.style ) {
	jQuery.attrHooks.style = {
		get: function( elem ) {
			return elem.style.cssText;
		},
		set: function( elem, value ) {
			return (elem.style.cssText = "" + value);
		}
	};
}

// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !jQuery.support.optSelected ) {

	jQuery.propHooks.selected = {
		get: function( elem ) {
			var parent = elem.parentNode;

			if ( parent ) {
				parent.selectedIndex;

				// TODO: We may need an attrHook for selected that simply defers to prop?
				// The attr is undefined if the option is created with createElement and not on the DOM

				// Make sure that it also works with optgroups, see #5701
				if ( parent.parentNode ) {
					parent.parentNode.selectedIndex;
				}
			}
		}
	};
}

})( jQuery );<commit_msg>Style formatting<commit_after>(function( jQuery ) {

var rclass = /[\n\t\r]/g,
	rspaces = /\s+/,
	rreturn = /\r/g,
	rtype = /^(?:button|input)$/i,
	rfocusable = /^(?:button|input|object|select|textarea)$/i,
	rclickable = /^a(?:rea)?$/i,
	rradiocheck = /^(?:radio|checkbox)$/i,
	formHook;

jQuery.fn.extend({
	attr: function( name, value ) {
		return jQuery.access( this, name, value, true, jQuery.attr );
	},

	removeAttr: function( name ) {
		return this.each(function() {
			if ( this.nodeType === 1 ) {
				jQuery.removeAttr( this, name );
			}
		});
	},

	prop: function( name, value ) {
		return jQuery.access( this, name, value, true, jQuery.prop );
	},

	removeProp: function( name ) {
		return this.each(function() {
			// try/catch handles cases where IE balks (such as removing a property on window)
			try {
				this[ name ] = undefined;
				delete this[ name ];
			} catch( e ) {}
		});
	},

	addClass: function( value ) {
		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.addClass( value.call(this, i, self.attr("class") || "") );
			});
		}

		if ( value && typeof value === "string" ) {
			var classNames = (value || "").split( rspaces );

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 ) {
					if ( !elem.className ) {
						elem.className = value;

					} else {
						var className = " " + elem.className + " ",
							setClass = elem.className;

						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
								setClass += " " + classNames[c];
							}
						}
						elem.className = jQuery.trim( setClass );
					}
				}
			}
		}

		return this;
	},

	removeClass: function( value ) {
		if ( jQuery.isFunction(value) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.removeClass( value.call(this, i, self.attr("class")) );
			});
		}

		if ( (value && typeof value === "string") || value === undefined ) {
			var classNames = (value || "").split( rspaces );

			for ( var i = 0, l = this.length; i < l; i++ ) {
				var elem = this[i];

				if ( elem.nodeType === 1 && elem.className ) {
					if ( value ) {
						var className = (" " + elem.className + " ").replace(rclass, " ");
						for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
							className = className.replace(" " + classNames[c] + " ", " ");
						}
						elem.className = jQuery.trim( className );

					} else {
						elem.className = "";
					}
				}
			}
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var type = typeof value,
			isBool = typeof stateVal === "boolean";

		if ( jQuery.isFunction( value ) ) {
			return this.each(function(i) {
				var self = jQuery(this);
				self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
			});
		}

		return this.each(function() {
			if ( type === "string" ) {
				// toggle individual class names
				var className,
					i = 0,
					self = jQuery( this ),
					state = stateVal,
					classNames = value.split( rspaces );

				while ( (className = classNames[ i++ ]) ) {
					// check each className given, space seperated list
					state = isBool ? state : !self.hasClass( className );
					self[ state ? "addClass" : "removeClass" ]( className );
				}

			} else if ( type === "undefined" || type === "boolean" ) {
				if ( this.className ) {
					// store className if set
					jQuery._data( this, "__className__", this.className );
				}

				// toggle whole className
				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
			}
		});
	},

	hasClass: function( selector ) {
		var className = " " + selector + " ";
		for ( var i = 0, l = this.length; i < l; i++ ) {
			if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
				return true;
			}
		}

		return false;
	},

	val: function( value ) {
		if ( !arguments.length ) {
			var elem = this[0];

			if ( elem ) {
				if ( jQuery.nodeName( elem, "option" ) ) {
					// attributes.value is undefined in Blackberry 4.7 but
					// uses .value. See #6932
					var val = elem.attributes.value;
					return !val || val.specified ? elem.value : elem.text;
				}

				// We need to handle select boxes special
				if ( jQuery.nodeName( elem, "select" ) ) {
					var index = elem.selectedIndex,
						values = [],
						options = elem.options,
						one = elem.type === "select-one";

					// Nothing was selected
					if ( index < 0 ) {
						return null;
					}

					// Loop through all the selected options
					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
						var option = options[ i ];

						// Don't return options that are disabled or in a disabled optgroup
						if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
								(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {

							// Get the specific value for the option
							value = jQuery(option).val();

							// We don't need an array for one selects
							if ( one ) {
								return value;
							}

							// Multi-Selects return an array
							values.push( value );
						}
					}

					// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
					if ( one && !values.length && options.length ) {
						return jQuery( options[ index ] ).val();
					}

					return values;
				}

				// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
				if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
					return elem.getAttribute("value") === null ? "on" : elem.value;
				}

				// Everything else, we just grab the value
				return (elem.value || "").replace(rreturn, "");

			}

			return undefined;
		}

		var isFunction = jQuery.isFunction(value);

		return this.each(function(i) {
			var self = jQuery(this), val = value;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( isFunction ) {
				val = value.call(this, i, self.val());
			}

			// Treat null/undefined as ""; convert numbers to string
			if ( val == null ) {
				val = "";
			} else if ( typeof val === "number" ) {
				val += "";
			} else if ( jQuery.isArray(val) ) {
				val = jQuery.map(val, function (value) {
					return value == null ? "" : value + "";
				});
			}

			if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
				this.checked = jQuery.inArray( self.val(), val ) >= 0;

			} else if ( jQuery.nodeName( this, "select" ) ) {
				var values = jQuery.makeArray(val);

				jQuery( "option", this ).each(function() {
					this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
				});

				if ( !values.length ) {
					this.selectedIndex = -1;
				}

			} else {
				this.value = val;
			}
		});
	}
});

jQuery.extend({
	attrFn: {
		val: true,
		css: true,
		html: true,
		text: true,
		data: true,
		width: true,
		height: true,
		offset: true
	},

	attrFix: {
		// Always normalize to ensure hook usage
		tabindex: "tabIndex"
	},

	attr: function( elem, name, value, pass ) {
		var nType = elem.nodeType;

		// don't get/set attributes on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return undefined;
		}

		if ( pass && name in jQuery.attrFn ) {
			return jQuery( elem )[ name ]( value );
		}

		var ret, hooks,
			notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

		// Normalize the name if needed
		name = notxml && jQuery.attrFix[ name ] || name;

		// Get the appropriate hook, or the formHook
		// if getSetAttribute is not supported and we have form objects in IE6/7
		hooks = formHook && ( name === "name" || elem.nodeName === "FORM" ) ?
					formHook( name ) :
					jQuery.attrHooks[ name ];

		if ( value !== undefined ) {

			if ( value === null ) {
				jQuery.removeAttr( elem, name );
				return undefined;

			} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value )) !== undefined ) {
				return ret;

			} else {
				elem.setAttribute( name, value );
				return value;
			}

		} else {

			if ( hooks && "get" in hooks && notxml ) {
				return hooks.get( elem );

			} else {

				ret = elem.getAttribute( name );

				// Non-existent attributes return null, we normalize to undefined
				return ret === null ?
					undefined :
					ret;
			}
		}
	},

	removeAttr: function( elem, name ) {
		name = jQuery.attrFix[ name ] || name;

		if ( jQuery.support.getSetAttribute ) {
			elem.removeAttribute( name );
		} else {
			// Set to default empty string
			elem.setAttribute( name, "" );
			// Attempt to remove completely with DOM level 1
			elem.removeAttributeNode( elem.getAttributeNode( name ) );
		}
	},

	attrHooks: {
		type: {
			set: function( elem, value ) {
				// We can't allow the type property to be changed (since it causes problems in IE)
				if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
					jQuery.error( "type property can't be changed" );
				}
			}
		},
		tabIndex: {
			get: function( elem ) {
				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				var attributeNode = elem.getAttributeNode("tabIndex");

				return attributeNode && attributeNode.specified ?
					parseInt( attributeNode.value, 10 ) :
					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
						0 :
						undefined;
			}
		}
	},

	propFix: {},

	prop: function( elem, name, value ) {
		var nType = elem.nodeType;

		// don't get/set properties on text, comment and attribute nodes
		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
			return undefined;
		}

		var ret, hooks,
			notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

		// Try to normalize/fix the name
		name = notxml && jQuery.propFix[ name ] || name;

		hooks = jQuery.propHooks[ name ];

		if ( value !== undefined ) {
			if ( hooks && "set" in hooks && (ret = hooks.set( elem, value )) !== undefined ) {
				return ret;

			} else {
				return (elem[ name ] = value);
			}

		} else {
			if ( hooks && "get" in hooks && (ret = hooks.get( elem )) !== undefined ) {
				return ret;

			} else {
				return elem[ name ];
			}
		}
	},

	propHooks: {}
});

// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !jQuery.support.getSetAttribute ) {
	jQuery.attrFix = jQuery.extend( jQuery.attrFix, {
		"for": "htmlFor",
		"class": "className",
		readonly: "readOnly",
		maxlength: "maxLength",
		cellspacing: "cellSpacing",
		rowspan: "rowSpan",
		colspan: "colSpan",
		usemap: "useMap",
		frameborder: "frameBorder"
	});

	// Use this for any attribute on a form in IE6/7
	// And the name attribute
	formHook = function( name ) {
		return jQuery.attrHooks[ name ] || {
			get: function( elem ) {
				var ret = elem.getAttributeNode( name );
				// Return undefined if not specified instead of empty string
				return ret && ret.specified ?
					ret.nodeValue :
					undefined;
			},
			set: function( elem, value ) {
				// Check form objects in IE (multiple bugs related)
				// Only use nodeValue if the attribute node exists on the form
				var ret = elem.getAttributeNode( name );
				if ( ret ) {
					ret.nodeValue = value;
				} else {
					elem.setAttribute( name, value );
				}
				return value;
			}
		};
	};
}

// Remove certain attrs if set to false
jQuery.each([ "selected", "checked", "readonly", "disabled" ], function( i, name ) {
	name = jQuery.attrFix[ name ] || name;

	jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
		set: function( elem, value ) {
			if ( !value ) { // '', undefined, false, null will remove attr
				jQuery.removeAttr( elem, name );
				return false;
			}

			elem.setAttribute( name, value );
			return value;
		}
	});
});

// Some attributes require a special call on IE
if ( !jQuery.support.hrefNormalized ) {
	jQuery.each([ "href", "src", "style" ], function( i, name ) {
		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
			get: function( elem ) {
				return elem.getAttribute( name, 2 );
			}
		});
	});
}

if ( !jQuery.support.style ) {
	jQuery.attrHooks.style = {
		get: function( elem ) {
			return elem.style.cssText;
		},
		set: function( elem, value ) {
			return (elem.style.cssText = "" + value);
		}
	};
}

// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !jQuery.support.optSelected ) {

	jQuery.propHooks.selected = {
		get: function( elem ) {
			var parent = elem.parentNode;

			if ( parent ) {
				parent.selectedIndex;

				// TODO: We may need an attrHook for selected that simply defers to prop?
				// The attr is undefined if the option is created with createElement and not on the DOM

				// Make sure that it also works with optgroups, see #5701
				if ( parent.parentNode ) {
					parent.parentNode.selectedIndex;
				}
			}
		}
	};
}

})( jQuery );<|endoftext|>


<commit_before>/**
 * Improves Backbone Model support when nested attributes are used.
 * get() and set() can take paths e.g. 'user.name'
 */
;(function(Backbone) {

    /**
     * Takes a nested object and returns a shallow object keyed with the path names
     * e.g. { "level1.level2": "value" }
     *
     * @param  {Object}      Nested object e.g. { level1: { level2: 'value' } }
     * @return {Object}      Shallow object with path names e.g. { 'level1.level2': 'value' }
     */
    function objToPaths(obj) {
        var ret = {};

        for (var key in obj) {
            var val = obj[key];

            if (val && val.constructor === Object) {
                //Recursion for embedded objects
                var obj2 = objToPaths(val);

                for (var key2 in obj2) {
                    var val2 = obj2[key2];

                    ret[key+'.'+key2] = val2;
                }
            } else {
                ret[key] = val;
            }
        }

        return ret;
    }

    /**
     * @param {Object}  Object to fetch attribute from
     * @param {String}  Object path e.g. 'user.name'
     * @return {Mixed}
     */
    function getNested(obj, path) {
        var fields = path.split(".");
        var result = obj;
        for (var i = 0, n = fields.length; i < n; i++) {
            result = result[fields[i]];

            if (typeof result === 'undefined') {
                return result;
            }
        }
        return result;
    }

    /**
     * @param {Object}  Object to fetch attribute from
     * @param {String}  Object path e.g. 'user.name'
     * @param {Mixed}   Value to set
     */
    function setNested(obj, path, val) {
        var fields = path.split(".");
        var result = obj;
        for (var i = 0, n = fields.length; i < n; i++) {
            var field = fields[i];

            //If the last in the path, set the value
            if (i === n - 1) {
                result[field] = val;
            } else {
                //Create the child object if it doesn't exist
                if (typeof result[field] === 'undefined') {
                    result[field] = {};
                }

                //Move onto the next part of the path
                result = result[field];
            }
        }
    }

    var DeepModel = Backbone.Model.extend({

        // Override get
        // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
        get: function(attr) {
            return getNested(this.attributes, attr);
        },

        // Override set
        // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
        set: function(key, value, options) {
            var attrs, attr;

            if (_.isObject(key) || key == null) {
                attrs = key;
                options = value;
            } else {
                attrs = {};
                attrs[key] = value;
            }

            // Extract attributes and options.
            options || (options = {});
            if (!attrs) return this;
            if (attrs.attributes) attrs = attrs.attributes;
            if (options.unset) for (attr in attrs) attrs[attr] = void 0;
            var now = this.attributes, escaped = this._escapedAttributes;

            // Run validation.
            if (!this._validate(attrs, options)) return false;

            // Check for changes of `id`.
            if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];

            // We're about to start triggering change events.
            var alreadyChanging = this._changing;
            this._changing = true;

            //START CUSTOM CODE
            var self = this;

            this._changed = {};
            function performSet(attrs) {
                // Update attributes.
                for (var attr in attrs) {
                    var val = attrs[attr];

                    if (val && val.constructor === Object) {
                        //Recursion for nested objects
                        performSet(val);
                    } else {
                        if (!_.isEqual(getNested(now, attr), val)) {
                            setNested(now, attr, val);
                            //deleteNested(escaped, attr); //TODO: Create this and use instead of setNested line below?
                            setNested(escaped, attr, undefined);

                            // This needs to be set for things like changedAttributes() to work
                            self._changed[attr] = val;
                            if (!options.silent) self.trigger('change:' + attr, self, val, options);
                        }
                    }
                }
            }

            attrs = objToPaths(attrs);

            performSet(attrs)

            //END CUSTOM CODE

            this._changing = false;
            // Fire the `"change"` event, if the model has been changed.
            if (!alreadyChanging && !options.silent && this._changed)
            {
                // the change:attribute events have allready been fired. This will prevent them from being sent again.
                var backupTrigger = this.trigger
                var self = this;
                this.trigger = function(evt){
                    if (evt == 'change')
                    {
                        backupTrigger.apply(self, arguments)
                    }
                }
                this.change(options);
                this.trigger = backupTrigger;
            }
            return this;
        },

        // Override has
        has: function(attr) {
            return getNested(this.attributes, attr) != null;
        },

        // Remove an attribute from the model, firing `"change"` unless you choose
        // to silence it. `unset` is a noop if the attribute doesn't exist.

        unset : function(attr, options) {
          //if (!(attr in this.attributes)) return this;
          // New code to check if attribute exists
          if (getNested(this.attributes, attr) === undefined) return this;
          options || (options = {});

          //var value = this.attributes[attr]; //Why is this needed?

          // Run validation.
          var validObj = {};
          validObj[attr] = void 0;
          if (!this._validate(attrs, options)) return false;

          // changedAttributes needs to know if an attribute has been unset.
          (this._unsetAttributes || (this._unsetAttributes = [])).push(attr);

          // Remove the attribute.

          //Test if path
          var path = attr.split('.')
          if(path.length === 1){
            delete this.attributes[attr];
            delete this._escapedAttributes[attr];
            if (attr == this.idAttribute) delete this.id;
          } else {
            //Get parent
            var deep_property = path[path.length -1];
            var parent = (path.slice(0,(path.length-1))).join('.')
            var parent_value = getNested(this.attributes, parent);
            var escaped_value = getNested(this._escapedAttributes, parent);
            delete parent_value[deep_property];
            delete escaped_value[deep_property];
          }
          this._changed = true;
          if (!options.silent) {
            this.trigger('change:' + attr, this, void 0, options);
            this.change(options);
          }

          return this;
        }

    });

    //Exports
    Backbone.DeepModel = DeepModel;

})(Backbone);
<commit_msg>There is a unit test that verifies that set is run even if the value passed in seems equal to the old value.<commit_after>/**
 * Improves Backbone Model support when nested attributes are used.
 * get() and set() can take paths e.g. 'user.name'
 */
;(function(Backbone) {

    /**
     * Takes a nested object and returns a shallow object keyed with the path names
     * e.g. { "level1.level2": "value" }
     *
     * @param  {Object}      Nested object e.g. { level1: { level2: 'value' } }
     * @return {Object}      Shallow object with path names e.g. { 'level1.level2': 'value' }
     */
    function objToPaths(obj) {
        var ret = {};

        for (var key in obj) {
            var val = obj[key];

            if (val && val.constructor === Object) {
                //Recursion for embedded objects
                var obj2 = objToPaths(val);

                for (var key2 in obj2) {
                    var val2 = obj2[key2];

                    ret[key+'.'+key2] = val2;
                }
            } else {
                ret[key] = val;
            }
        }

        return ret;
    }

    /**
     * @param {Object}  Object to fetch attribute from
     * @param {String}  Object path e.g. 'user.name'
     * @return {Mixed}
     */
    function getNested(obj, path) {
        var fields = path.split(".");
        var result = obj;
        for (var i = 0, n = fields.length; i < n; i++) {
            result = result[fields[i]];

            if (typeof result === 'undefined') {
                return result;
            }
        }
        return result;
    }

    /**
     * @param {Object}  Object to fetch attribute from
     * @param {String}  Object path e.g. 'user.name'
     * @param {Mixed}   Value to set
     */
    function setNested(obj, path, val) {
        var fields = path.split(".");
        var result = obj;
        for (var i = 0, n = fields.length; i < n; i++) {
            var field = fields[i];

            //If the last in the path, set the value
            if (i === n - 1) {
                result[field] = val;
            } else {
                //Create the child object if it doesn't exist
                if (typeof result[field] === 'undefined') {
                    result[field] = {};
                }

                //Move onto the next part of the path
                result = result[field];
            }
        }
    }

    var DeepModel = Backbone.Model.extend({

        // Override get
        // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
        get: function(attr) {
            return getNested(this.attributes, attr);
        },

        // Override set
        // Supports nested attributes via the syntax 'obj.attr' e.g. 'author.user.name'
        set: function(key, value, options) {
            var attrs, attr;

            if (_.isObject(key) || key == null) {
                attrs = key;
                options = value;
            } else {
                attrs = {};
                attrs[key] = value;
            }

            // Extract attributes and options.
            options || (options = {});
            if (!attrs) return this;
            if (attrs.attributes) attrs = attrs.attributes;
            if (options.unset) for (attr in attrs) attrs[attr] = void 0;
            var now = this.attributes, escaped = this._escapedAttributes;

            // Run validation.
            if (!this._validate(attrs, options)) return false;

            // Check for changes of `id`.
            if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];

            // We're about to start triggering change events.
            var alreadyChanging = this._changing;
            this._changing = true;

            //START CUSTOM CODE
            var self = this;

            this._changed = {};
            function performSet(attrs) {
                // Update attributes.
                for (var attr in attrs) {
                    var val = attrs[attr];

                    if (val && val.constructor === Object) {
                        //Recursion for nested objects
                        performSet(val);
                    } else {
                        setNested(now, attr, val);
                        //deleteNested(escaped, attr); //TODO: Create this and use instead of setNested line below?
                        setNested(escaped, attr, undefined);

                        // This needs to be set for things like changedAttributes() to work
                        self._changed[attr] = val;
                        if (!options.silent) self.trigger('change:' + attr, self, val, options);
                    }
                }
            }

            attrs = objToPaths(attrs);

            performSet(attrs)

            //END CUSTOM CODE

            this._changing = false;
            // Fire the `"change"` event, if the model has been changed.
            if (!alreadyChanging && !options.silent && this._changed)
            {
                // the change:attribute events have allready been fired. This will prevent them from being sent again.
                var backupTrigger = this.trigger
                var self = this;
                this.trigger = function(evt){
                    if (evt == 'change')
                    {
                        backupTrigger.apply(self, arguments)
                    }
                }
                this.change(options);
                this.trigger = backupTrigger;
            }
            return this;
        },

        // Override has
        has: function(attr) {
            return getNested(this.attributes, attr) != null;
        },

        // Remove an attribute from the model, firing `"change"` unless you choose
        // to silence it. `unset` is a noop if the attribute doesn't exist.

        unset : function(attr, options) {
          //if (!(attr in this.attributes)) return this;
          // New code to check if attribute exists
          if (getNested(this.attributes, attr) === undefined) return this;
          options || (options = {});

          //var value = this.attributes[attr]; //Why is this needed?

          // Run validation.
          var validObj = {};
          validObj[attr] = void 0;
          if (!this._validate(attrs, options)) return false;

          // changedAttributes needs to know if an attribute has been unset.
          (this._unsetAttributes || (this._unsetAttributes = [])).push(attr);

          // Remove the attribute.

          //Test if path
          var path = attr.split('.')
          if(path.length === 1){
            delete this.attributes[attr];
            delete this._escapedAttributes[attr];
            if (attr == this.idAttribute) delete this.id;
          } else {
            //Get parent
            var deep_property = path[path.length -1];
            var parent = (path.slice(0,(path.length-1))).join('.')
            var parent_value = getNested(this.attributes, parent);
            var escaped_value = getNested(this._escapedAttributes, parent);
            delete parent_value[deep_property];
            delete escaped_value[deep_property];
          }
          this._changed = true;
          if (!options.silent) {
            this.trigger('change:' + attr, this, void 0, options);
            this.change(options);
          }

          return this;
        }

    });

    //Exports
    Backbone.DeepModel = DeepModel;

})(Backbone);
<|endoftext|>


<commit_before>class Background {
  constructor() {
    this.listeners = {};
    this.lastTab = null;
    this.tabList = [];

    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      if (!message.background) {
        return;
      }
      console.log('Background: got message:', message);

      let readMessage;
      switch (message.action) {
        case 'getTabs':
          readMessage = this.getTabs();
          break;
        case 'openPopup':
          readMessage = this.onPopupOpen();
          break;
        case 'closePopup':
          readMessage = this.onPopupClose();
          break;
        default:
          throw new Error(
            `Background got an unexpected action: ${message.action}`);
      }

      readMessage.then(reply => sendResponse(reply))
        .catch(error => {
          console.error('Background: error reading message', error);
        });

      return true;
    });
  }

  onPopupOpen() {
    this.addListeners({
      onUpdated: this.onTabsUpdated,
      onRemoved: this.onTabRemoved,
    });
    return Promise.resolve();
  }

  onPopupClose() {
    this.removeListeners();
    return Promise.resolve();
  }

  sendToPopup(message) {
    return new Promise((resolve, reject) => {
      const id = undefined;
      const options = undefined;
      chrome.runtime.sendMessage(
        id,
        {
          popup: true,
          ...message,
        },
        options,
        (result) => {
          if (chrome.runtime.lastError) {
            console.log(
              'background: got error:', chrome.runtime.lastError);
            reject(chrome.runtime.lastError);
          } else {
            resolve(result);
          }
        }
      );
    });
  }

  visitListeners(visit) {
    Object.keys(this.listeners).forEach(eventName => {
      const callback = this.listeners[eventName];
      if (!callback) {
        throw new Error(
          `eventName "${eventName}" is not a valid listener callback; ` +
          `value=${callback}`);
      }
      const event = chrome.tabs[eventName];
      if (!event) {
        throw new Error(
          `chrome.tabs["${eventName}"] is not a valid event; value=${event}`);
      }
      visit({event, callback});
    });
  }

  addListeners(listeners) {
    this.listeners = listeners;
    this.visitListeners(({event, callback}) => {
      event.addListener(callback);
    });
  }

  removeListeners() {
    this.visitListeners(({event, callback}) => {
      event.removeListener(callback);
    });
  }

  onTabsUpdated = (tabId, changeInfo, tab) => {
    console.log(`background: Tab ${tab.id} was updated`, changeInfo);
    if (this.lastTab.id === tabId) {
      this.lastTab = tab;
    }
    this.tabList = this.tabList.map(
      oldTab => oldTab.id === tab.id ? tab : oldTab);

    this.sendToPopup({
      action: 'tabListChanged',
      data: {tabs: this.tabList},
    });
  }

  onTabRemoved = (tabId) => {
    console.log(`background: Tab ${tabId} was removed`);
    if (this.lastTab.id === tabId) {
      this.lastTab = null;
    }

    this.tabList = this.tabList.filter(tab => tab.id !== tabId);

    this.sendToPopup({
      action: 'tabListChanged',
      data: {tabs: this.tabList},
    });
  }

  queryTabs(query) {
    return new Promise((resolve, reject) => {
      chrome.tabs.query(query, tabs => {
        if (chrome.runtime.lastError) {
          return reject(chrome.runtime.lastError);
        }
        resolve(tabs);
      });
    });
  }

  getTabs = () => {
    return Promise.all([
      this.queryTabs({active: true, currentWindow: true}),
      this.queryTabs({audible: true}),
    ])
      .then(([activeTabs, audibleTabs]) => {
        const newTabList = [];
        if (this.lastTab) {
          // Put the last viewed site at the top of the list so you
          // can easily jump between what you're working on and what
          // you're listening to.
          newTabList.push(this.lastTab);
        }

        function addUniqueTabs(tabList) {
          const existingIds = new Set(newTabList.map(tab => tab.id));
          tabList.forEach(tab => {
            if (!existingIds.has(tab.id)) {
              newTabList.push(tab);
            }
          });
        }

        addUniqueTabs(audibleTabs);
        // Keep inaudible tabs around since the music may have just been paused
        addUniqueTabs(this.tabList);

        while (newTabList.length > 10) {
          newTabList.pop();
        }

        this.tabList = newTabList;
        this.lastTab = activeTabs[0];

        return this.tabList;
      })
      .catch((error) => {
        console.log('background: getTabs: error:', error);
      });
  }

  listen() {
    console.log('background: listening for info about audible websites');
  }
}

const background = new Background();
background.listen();
<commit_msg>Switching to the previous tab works after closing/re-opening the popup now<commit_after>class Background {
  constructor() {
    this.visitedTabs = [];
    this.tabList = [];
    this.popupIsOpen = false;

    chrome.tabs.onActivated.addListener(this.onActivated);
    chrome.tabs.onUpdated.addListener(this.onTabsUpdated);
    chrome.tabs.onRemoved.addListener(this.onTabRemoved);

    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
      if (!message.background) {
        return;
      }
      console.log('Background: got message:', message);

      let readMessage;
      switch (message.action) {
        case 'getTabs':
          readMessage = this.getTabsForPopup();
          break;
        case 'openPopup':
          readMessage = this.onPopupOpen();
          break;
        case 'closePopup':
          readMessage = this.onPopupClose();
          break;
        default:
          throw new Error(
            `Background got an unexpected action: ${message.action}`);
      }

      readMessage.then(reply => sendResponse(reply))
        .catch(error => {
          console.error('Background: error reading message', error);
        });

      return true;
    });
  }

  onPopupOpen() {
    this.popupIsOpen = true;
    return Promise.resolve();
  }

  onPopupClose() {
    this.popupIsOpen = false;
    return Promise.resolve();
  }

  sendToPopup(message) {
    return new Promise((resolve, reject) => {
      const id = undefined;
      const options = undefined;
      chrome.runtime.sendMessage(
        id,
        {
          popup: true,
          ...message,
        },
        options,
        (result) => {
          if (chrome.runtime.lastError) {
            console.log(
              'background: got error:', chrome.runtime.lastError);
            reject(chrome.runtime.lastError);
          } else {
            resolve(result);
          }
        }
      );
    });
  }

  onTabsUpdated = (tabId, changeInfo, tab) => {
    console.log(`background: Tab ${tab.id} was updated`, changeInfo);
    this.visitedTabs = this.visitedTabs.map(
      oldTab => oldTab.id === tab.id ? tab : oldTab);
    this.tabList = this.tabList.map(
      oldTab => oldTab.id === tab.id ? tab : oldTab);

    if (this.popupIsOpen) {
      this.sendToPopup({
        action: 'tabListChanged',
        data: {tabs: this.tabList},
      });
    }
  }

  onTabRemoved = (tabId) => {
    console.log(`background: Tab ${tabId} was removed`);
    this.visitedTabs = this.visitedTabs.filter(tab => tab.id !== tabId);
    this.tabList = this.tabList.filter(tab => tab.id !== tabId);

    if (this.popupIsOpen) {
      this.sendToPopup({
        action: 'tabListChanged',
        data: {tabs: this.tabList},
      });
    }
  }

  onActivated = (activeInfo) => {
    const {tabId} = activeInfo
    this.getTab(tabId).then(tab => {
      this.visitedTabs.unshift(tab);
      if (this.visitedTabs.length > 2) {
        this.visitedTabs.pop();
      }
      console.log(`background: Active tab changed: ${tab.id}`);
    });
  }

  getTab(tabId) {
    return new Promise((resolve, reject) => {
      chrome.tabs.get(tabId, tab => {
        if (chrome.runtime.lastError) {
          return reject(chrome.runtime.lastError);
        }
        resolve(tab);
      });
    });
  }

  queryTabs(query) {
    return new Promise((resolve, reject) => {
      chrome.tabs.query(query, tabs => {
        if (chrome.runtime.lastError) {
          return reject(chrome.runtime.lastError);
        }
        resolve(tabs);
      });
    });
  }

  getTabsForPopup = () => {
    return this.queryTabs({audible: true})
      .then((audibleTabs) => {
        const newTabList = [];
        if (this.visitedTabs[1]) {
          // Put the last viewed site at the top of the list so you
          // can easily jump between what you're working on and what
          // you're listening to.
          newTabList.push(this.visitedTabs[1]);
        }

        function addUniqueTabs(tabList) {
          const existingIds = new Set(newTabList.map(tab => tab.id));
          tabList.forEach(tab => {
            if (!existingIds.has(tab.id)) {
              newTabList.push(tab);
            }
          });
        }

        addUniqueTabs(audibleTabs);
        // Keep inaudible tabs around since the music may have just been paused
        addUniqueTabs(this.tabList);

        while (newTabList.length > 10) {
          newTabList.pop();
        }

        this.tabList = newTabList;
        return this.tabList;
      })
      .catch((error) => {
        console.log('background: getTabsForPopup: error:', error);
      });
  }

  listen() {
    console.log('background: listening for info about audible websites');
  }
}

const background = new Background();
background.listen();
<|endoftext|>


<commit_before>(function () {
  "use strict";

  var currentIndex = [];

  function getCurrentActiveTab() {
    chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
      console.debug(tabs[0].windowId + ' - chrome.tabs.query - set currentIndex = index: ' + tabs[0].index); // debug
      currentIndex[tabs[0].windowId] = tabs[0].index;
    });
    // fallback
    chrome.windows.getCurrent(function(window) {
      if (!Number.isInteger(currentIndex[window.id])) {
        // last position (default behavior)
        chrome.tabs.query({currentWindow: true}, function (tabs) {
          console.debug(tabs[0].windowId + ' - fallback - set currentIndex = tabs.length: ' + tabs.length); // debug
          currentIndex[tabs[0].windowId] = tabs.length;
        });
      }
    });
  }

  function moveIt(tab, event) {
    console.debug('tab created');
    console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - get new tab.index: ' + tab.index + ' (' + tab.title + ')'); // debug
    console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - get currentIndex: ' + currentIndex[tab.windowId]); // debug
    if (Number.isInteger(currentIndex[tab.windowId])) {
      var moveToIndex = currentIndex[tab.windowId] + 1;
      if (tab.index == moveToIndex) {
        console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - tab.index: ' + tab.index + ' == moveToIndex: ' + moveToIndex + ' (nothing to do)'); // debug
        return;
      }
      console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - move to: ' + moveToIndex);
      chrome.tabs.move(tab.id, {
        index: moveToIndex
      });
    }
    console.debug('/ tab created');
  }

  var eventOnMoved = function (tabId, moveInfo) {
    console.debug(moveInfo.windowId + ' - chrome.tabs.onMoved - set currentIndex = moveInfo.toIndex: ' + moveInfo.toIndex); // debug
    currentIndex[moveInfo.windowId] = moveInfo.toIndex;
  }

  /**
   * Remember the current tab index
   */
  // on extension install/update
  chrome.runtime.onInstalled.addListener(function(details) {
    if (details.reason == 'install' || details.reason == 'update') {
      console.debug('chrome.runtime.onInstalled'); // debug
      getCurrentActiveTab();
    }
  });
  // on extension enabled
  chrome.management.onEnabled.addListener(function(ExtensionInfo) {
    if (ExtensionInfo.id == chrome.runtime.id) {
      console.debug('chrome.management.onEnabled'); // debug
      getCurrentActiveTab();
    }
  });
  // on window focused
  chrome.windows.onFocusChanged.addListener(function(windowId) {
    // https://developer.chrome.com/extensions/windows#property-WINDOW_ID_NONE
    if (windowId != -1 && windowId != -2) {
      chrome.tabs.getSelected(chrome.tabs.windowId, function(tab) {
        console.debug(windowId + ' - chrome.windows.onFocusChanged - set currentIndex = tab.index: ' + tab.index); // debug
        currentIndex[windowId] = tab.index;
      });
    }
  });
  // on tab activated
  chrome.tabs.onActivated.addListener(function(activeInfo) {
    if (chrome.runtime.lastError) {
      console.log(chrome.runtime.lastError.message);
    } else {
      chrome.tabs.get(activeInfo.tabId, function(tab) {
        console.debug(tab.windowId + ' - chrome.tabs.onActivated - set currentIndex = tab.index: ' + tab.index); // debug
        currentIndex[tab.windowId] = tab.index;
      });
    }
  });
  // on tab manually moved
  chrome.tabs.onMoved.addListener(eventOnMoved);

  /**
   * Move new tab after the current
   */
  // on created
  chrome.tabs.onCreated.addListener(function(tab) {
    chrome.tabs.onMoved.removeListener(eventOnMoved);
    moveIt(tab, 'onCreated'); // Windows: OK / Mac OS: ?
    chrome.tabs.onMoved.addListener(eventOnMoved);
  });
  // on updated
  /*
  chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    moveIt(tab, 'onUpdated');
  });
  */
})();<commit_msg>Cleaning code<commit_after>(function () {
  "use strict";

  var currentIndex = [];

  function getCurrentActiveTab() {
    chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
      console.debug(tabs[0].windowId + ' - chrome.tabs.query - set currentIndex = index: ' + tabs[0].index);
      currentIndex[tabs[0].windowId] = tabs[0].index;
    });
    // fallback
    chrome.windows.getCurrent(function(window) {
      if (!Number.isInteger(currentIndex[window.id])) {
        // last position (default behavior)
        chrome.tabs.query({currentWindow: true}, function (tabs) {
          console.debug(tabs[0].windowId + ' - fallback - set currentIndex = tabs.length: ' + tabs.length);
          currentIndex[tabs[0].windowId] = tabs.length;
        });
      }
    });
  }

  function moveIt(tab, event) {
    console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - get new tab.index: ' + tab.index);
    console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - get currentIndex: ' + currentIndex[tab.windowId]);
    if (Number.isInteger(currentIndex[tab.windowId])) {
      var moveToIndex = currentIndex[tab.windowId] + 1;
      if (tab.index == moveToIndex) {
        console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - tab.index: ' + tab.index + ' == moveToIndex: ' + moveToIndex + ' (nothing to do)');
        return;
      }
      console.debug(tab.windowId + ' - chrome.tabs.' + event + ' - move to: ' + moveToIndex);
      chrome.tabs.move(tab.id, {
        index: moveToIndex
      });
    }
  }

  var eventOnMoved = function (tabId, moveInfo) {
    console.debug(moveInfo.windowId + ' - chrome.tabs.onMoved - set currentIndex = moveInfo.toIndex: ' + moveInfo.toIndex);
    currentIndex[moveInfo.windowId] = moveInfo.toIndex;
  }

  /**
   * Remember the current tab index
   */
  // on extension install/update
  chrome.runtime.onInstalled.addListener(function(details) {
    if (details.reason == 'install' || details.reason == 'update') {
      console.debug('chrome.runtime.onInstalled');
      getCurrentActiveTab();
    }
  });
  // on extension enabled
  chrome.management.onEnabled.addListener(function(ExtensionInfo) {
    if (ExtensionInfo.id == chrome.runtime.id) {
      console.debug('chrome.management.onEnabled');
      getCurrentActiveTab();
    }
  });
  // on window focused
  chrome.windows.onFocusChanged.addListener(function(windowId) {
    // https://developer.chrome.com/extensions/windows#property-WINDOW_ID_NONE
    if (windowId != -1 && windowId != -2) {
      chrome.tabs.getSelected(chrome.tabs.windowId, function(tab) {
        console.debug(windowId + ' - chrome.windows.onFocusChanged - set currentIndex = tab.index: ' + tab.index);
        currentIndex[windowId] = tab.index;
      });
    }
  });
  // on tab activated
  chrome.tabs.onActivated.addListener(function(activeInfo) {
    if (chrome.runtime.lastError) {
      console.log(chrome.runtime.lastError.message);
    } else {
      chrome.tabs.get(activeInfo.tabId, function(tab) {
        console.debug(tab.windowId + ' - chrome.tabs.onActivated - set currentIndex = tab.index: ' + tab.index);
        currentIndex[tab.windowId] = tab.index;
      });
    }
  });
  // on tab manually moved
  chrome.tabs.onMoved.addListener(eventOnMoved);

  /**
   * Move new tab after the current
   */
  // on created
  chrome.tabs.onCreated.addListener(function(tab) {
    chrome.tabs.onMoved.removeListener(eventOnMoved);
    moveIt(tab, 'onCreated');
    chrome.tabs.onMoved.addListener(eventOnMoved);
  });
})();<|endoftext|>


<commit_before>import path from 'path';
import jetpack from 'fs-jetpack';
import {app, Menu, shell} from 'electron';
import createWindow from './helpers/window';
import { migrateSettings } from './migrate-settings';

const settings = require('electron-settings');
const appMenu = require('./menu');
const tray = require('./tray');

require('electron-dl')({saveAs: true});

migrateSettings();

if (process.env.NAME === 'test') {
  settings.deleteAll();
}

if (process.env.NAME === 'development') {
	require('electron-reload')(__dirname);
	require('electron-debug')({enabled: true});
}

let mainWindow;
let isQuitting = false;

const isAlreadyRunning = app.makeSingleInstance(() => {
	if (mainWindow) {
		if (mainWindow.isMinimized()) {
			mainWindow.restore();
		}

		mainWindow.show();
	}
});

if (isAlreadyRunning) {
	app.quit();
}

function createMainWindow() {
	const isDarkMode = settings.get('darkMode');

	const win = new createWindow('main', {
		title: app.getName(),
		show: false,
		width: 1300,
		height: 850,
		icon: process.platform === 'linux' && path.join(__dirname, 'static/Icon.png'),
		minWidth: 1000,
		minHeight: 700,
		alwaysOnTop: settings.get('alwaysOnTop'),
		titleBarStyle: 'hidden-inset',
		autoHideMenuBar: true,
		darkTheme: isDarkMode, // GTK+3
		//backgroundColor: isDarkMode ? '#192633' : '#fff',
		webPreferences: {
			preload: path.join(__dirname, 'browser.js'),
			nodeIntegration: true,
			plugins: true
		}
	});

  /**
	 * Trick to make the window draggable
   * https://github.com/electron/electron/pull/5557
   */
  const titleBarHack =
          'var div = document.createElement("div");' +
          'div.style.position = "absolute";' +
          'div.style.top = 0;' +
          'div.style.height = "23px";' +
          'div.style.width = "100%";' +
          'div.style["-webkit-app-region"] = "drag";' +
          'document.body.appendChild(div);';

  win.webContents.on('did-finish-load', function () {
    if (process.platform === 'darwin') {
      win.webContents.executeJavaScript(titleBarHack);
    }
  });

	if (process.platform === 'darwin') {
		win.setSheetOffset(40);
	}

	win.loadURL('file://' + __dirname + '/index.html');

	win.on('close', e => {
		if (!isQuitting) {
			e.preventDefault();

			if (process.platform === 'darwin') {
				app.hide();
			} else {
				win.hide();
			}
		}
	});

	return win;
}

app.on('ready', () => {
	Menu.setApplicationMenu(appMenu);
	mainWindow = createMainWindow();
	tray.create(mainWindow);

	const page = mainWindow.webContents;

	const argv = require('minimist')(process.argv.slice(1));

	page.on('dom-ready', () => {
		page.insertCSS(jetpack.read(path.join(__dirname, 'browser.css'), 'utf8'));
		page.insertCSS(jetpack.read(path.join(__dirname, 'themes/dark-mode.css'), 'utf8'));

		if (process.platform === 'darwin') {
			page.insertCSS(jetpack.read(path.join(__dirname, 'themes/osx-fix.css'), 'utf8'));
		}

		if (argv.minimize) {
			mainWindow.minimize();
		} else {
			mainWindow.show();
		}

    if (process.env.NAME === 'development') {
      mainWindow.openDevTools();
    }
	});
});

app.on('activate', () => {
	mainWindow.show();
});

app.on('before-quit', () => {
    isQuitting = true;
});

<commit_msg>e2e clean settings when main window opens<commit_after>import path from 'path';
import jetpack from 'fs-jetpack';
import {app, Menu, shell} from 'electron';
import createWindow from './helpers/window';
import { migrateSettings } from './migrate-settings';

const settings = require('electron-settings');
const appMenu = require('./menu');
const tray = require('./tray');

require('electron-dl')({saveAs: true});

migrateSettings();

if (process.env.NAME === 'development') {
	require('electron-reload')(__dirname);
	require('electron-debug')({enabled: true});
}

let mainWindow;
let isQuitting = false;

const isAlreadyRunning = app.makeSingleInstance(() => {
	if (mainWindow) {
		if (mainWindow.isMinimized()) {
			mainWindow.restore();
		}

		mainWindow.show();
	}
});

if (isAlreadyRunning) {
	app.quit();
}

function createMainWindow() {
  if (process.env.NAME === 'test') {
    settings.deleteAll();
  }

	const isDarkMode = settings.get('darkMode');

	const win = new createWindow('main', {
		title: app.getName(),
		show: false,
		width: 1300,
		height: 850,
		icon: process.platform === 'linux' && path.join(__dirname, 'static/Icon.png'),
		minWidth: 1000,
		minHeight: 700,
		alwaysOnTop: settings.get('alwaysOnTop'),
		titleBarStyle: 'hidden-inset',
		autoHideMenuBar: true,
		darkTheme: isDarkMode, // GTK+3
		//backgroundColor: isDarkMode ? '#192633' : '#fff',
		webPreferences: {
			preload: path.join(__dirname, 'browser.js'),
			nodeIntegration: true,
			plugins: true
		}
	});

  /**
	 * Trick to make the window draggable
   * https://github.com/electron/electron/pull/5557
   */
  const titleBarHack =
          'var div = document.createElement("div");' +
          'div.style.position = "absolute";' +
          'div.style.top = 0;' +
          'div.style.height = "23px";' +
          'div.style.width = "100%";' +
          'div.style["-webkit-app-region"] = "drag";' +
          'document.body.appendChild(div);';

  win.webContents.on('did-finish-load', function () {
    if (process.platform === 'darwin') {
      win.webContents.executeJavaScript(titleBarHack);
    }
  });

	if (process.platform === 'darwin') {
		win.setSheetOffset(40);
	}

	win.loadURL('file://' + __dirname + '/index.html');

	win.on('close', e => {
		if (!isQuitting) {
			e.preventDefault();

			if (process.platform === 'darwin') {
				app.hide();
			} else {
				win.hide();
			}
		}
	});

	return win;
}

app.on('ready', () => {
	Menu.setApplicationMenu(appMenu);
	mainWindow = createMainWindow();
	tray.create(mainWindow);

	const page = mainWindow.webContents;

	const argv = require('minimist')(process.argv.slice(1));

	page.on('dom-ready', () => {
		page.insertCSS(jetpack.read(path.join(__dirname, 'browser.css'), 'utf8'));
		page.insertCSS(jetpack.read(path.join(__dirname, 'themes/dark-mode.css'), 'utf8'));

		if (process.platform === 'darwin') {
			page.insertCSS(jetpack.read(path.join(__dirname, 'themes/osx-fix.css'), 'utf8'));
		}

		if (argv.minimize) {
			mainWindow.minimize();
		} else {
			mainWindow.show();
		}

    if (process.env.NAME === 'development') {
      mainWindow.openDevTools();
    }
	});
});

app.on('activate', () => {
	mainWindow.show();
});

app.on('before-quit', () => {
    isQuitting = true;
});

<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import Browser from 'components/browser'
import $ from 'clappr-zepto'

export function extend(parent, properties) {
  var constructor = function() {
    parent.prototype.constructor.apply(this, arguments)
    if (properties.constructor) {
      properties.constructor.apply(this, arguments)
    }
  }
  constructor.prototype = Object.create(parent.prototype)
  $.extend(constructor.prototype, properties)
  return constructor
}

export function formatTime(time) {
    if (!isFinite(time)) {
      return "--:--"
    }
    time = time * 1000
    time = parseInt(time/1000)
    var seconds = time % 60
    time = parseInt(time/60)
    var minutes = time % 60
    time = parseInt(time/60)
    var hours = time % 24
    var days = parseInt(time/24)
    var out = ""
    if (days && days > 0) {
      out += days + ":"
      if (hours < 1) out += "00:"
    }
    if (hours && hours > 0) out += ("0" + hours).slice(-2) + ":"
    out += ("0" + minutes).slice(-2) + ":"
    out += ("0" + seconds).slice(-2)
    return out.trim()
}

export var Fullscreen = {
  isFullscreen: function() {
    return (
      document.webkitFullscreenElement ||
      document.webkitIsFullScreen ||
      document.mozFullScreen ||
      !!document.msFullscreenElement
    )
  },
  requestFullscreen: function(el) {
    if(el.requestFullscreen) {
      el.requestFullscreen()
    } else if(el.webkitRequestFullscreen) {
      el.webkitRequestFullscreen()
    } else if(el.mozRequestFullScreen) {
      el.mozRequestFullScreen()
    } else if(el.msRequestFullscreen) {
      el.msRequestFullscreen()
    } else if (el.querySelector && el.querySelector("video") && el.querySelector("video").webkitEnterFullScreen) {
      el.querySelector("video").webkitEnterFullScreen()
    }
  },
  cancelFullscreen: function() {
    if(document.exitFullscreen) {
      document.exitFullscreen()
    } else if(document.webkitCancelFullScreen) {
      document.webkitCancelFullScreen()
    } else if(document.webkitExitFullscreen) {
      document.webkitExitFullscreen()
    } else if(document.mozCancelFullScreen) {
      document.mozCancelFullScreen()
    } else if(document.msExitFullscreen) {
      document.msExitFullscreen()
    }
  }
}

export class Config {

  static _defaultConfig() {
    return {
      volume: {
        value: 100,
        parse: parseInt
      }
    }
  }

  static _defaultValueFor(key) {
    try {
      return this._defaultConfig()[key]['parse'](this._defaultConfig()[key]['value'])
    } catch(e) {
      return undefined
    }
  }

  static _create_keyspace(key){
    return 'clappr.' + document.domain + '.' + key
  }

  static restore(key) {
    if (Browser.hasLocalstorage && localStorage[this._create_keyspace(key)]){
      return this._defaultConfig()[key]['parse'](localStorage[this._create_keyspace(key)])
    }
    return this._defaultValueFor(key)
  }

  static persist(key, value) {
    if (Browser.hasLocalstorage) {
      try {
        localStorage[this._create_keyspace(key)] = value
        return true
      } catch(e) {
        return false
      }
    }
  }
}

export function seekStringToSeconds(url) {
  var parts = url.match(/t=([0-9]*)(&|\/|$)/);
  if (parts && parts.length > 0) {
    return parseInt(parts[1], 10);
  } else {
    var seconds = 0;
    var factor = {'h': 3600, 'm': 60, 's': 1};
    parts = url.match(/[0-9]+[hms]+/g) || [];
    parts.forEach(function(el) {
      if (el) {
        var suffix = el[el.length - 1];
        var time = parseInt(el.slice(0, el.length - 1), 10);
        seconds += time * (factor[suffix]);
      }
    });
    return seconds;
   }
}

var idsCounter = {}

export function uniqueId(prefix) {
  idsCounter[prefix] || (idsCounter[prefix] = 0)
  var id = ++idsCounter[prefix]
  return prefix + id
}

export function isNumber(value) {
  return value - parseFloat(value) + 1 >= 0
}

export function currentScriptUrl() {
  var scripts = document.getElementsByTagName('script')
  return scripts[scripts.length - 1].src
}

export var requestAnimationFrame = (window.requestAnimationFrame ||
                            window.mozRequestAnimationFrame ||
                            window.webkitRequestAnimationFrame ||
                            function(fn) { window.setTimeout(fn, 1000/60) }).bind(window)

export var cancelAnimationFrame = (window.cancelAnimationFrame ||
                           window.mozCancelAnimationFrame ||
                           window.webkitCancelAnimationFrame ||
                           window.clearTimeout).bind(window)

export default {
  Config,
  Fullscreen,
  extend,
  formatTime,
  seekStringToSeconds,
  uniqueId,
  currentScriptUrl,
  isNumber,
  requestAnimationFrame,
  cancelAnimationFrame
}
<commit_msg>utils: improve extend method<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import Browser from 'components/browser'
import $ from 'clappr-zepto'

export function extend(parent, properties) {
  var pluginName = properties.name||""
  class MergedPlugin extends parent {
    constructor(args) {
      super(args)
      if (properties.initialize) {
        properties.initialize.apply(this, [args])
      }
    }
    get name(){
      return pluginName
    }
  }
  delete properties.name
  $.extend(MergedPlugin, properties)
  return MergedPlugin
}

export function formatTime(time) {
    if (!isFinite(time)) {
      return "--:--"
    }
    time = time * 1000
    time = parseInt(time/1000)
    var seconds = time % 60
    time = parseInt(time/60)
    var minutes = time % 60
    time = parseInt(time/60)
    var hours = time % 24
    var days = parseInt(time/24)
    var out = ""
    if (days && days > 0) {
      out += days + ":"
      if (hours < 1) out += "00:"
    }
    if (hours && hours > 0) out += ("0" + hours).slice(-2) + ":"
    out += ("0" + minutes).slice(-2) + ":"
    out += ("0" + seconds).slice(-2)
    return out.trim()
}

export var Fullscreen = {
  isFullscreen: function() {
    return (
      document.webkitFullscreenElement ||
      document.webkitIsFullScreen ||
      document.mozFullScreen ||
      !!document.msFullscreenElement
    )
  },
  requestFullscreen: function(el) {
    if(el.requestFullscreen) {
      el.requestFullscreen()
    } else if(el.webkitRequestFullscreen) {
      el.webkitRequestFullscreen()
    } else if(el.mozRequestFullScreen) {
      el.mozRequestFullScreen()
    } else if(el.msRequestFullscreen) {
      el.msRequestFullscreen()
    } else if (el.querySelector && el.querySelector("video") && el.querySelector("video").webkitEnterFullScreen) {
      el.querySelector("video").webkitEnterFullScreen()
    }
  },
  cancelFullscreen: function() {
    if(document.exitFullscreen) {
      document.exitFullscreen()
    } else if(document.webkitCancelFullScreen) {
      document.webkitCancelFullScreen()
    } else if(document.webkitExitFullscreen) {
      document.webkitExitFullscreen()
    } else if(document.mozCancelFullScreen) {
      document.mozCancelFullScreen()
    } else if(document.msExitFullscreen) {
      document.msExitFullscreen()
    }
  }
}

export class Config {

  static _defaultConfig() {
    return {
      volume: {
        value: 100,
        parse: parseInt
      }
    }
  }

  static _defaultValueFor(key) {
    try {
      return this._defaultConfig()[key]['parse'](this._defaultConfig()[key]['value'])
    } catch(e) {
      return undefined
    }
  }

  static _create_keyspace(key){
    return 'clappr.' + document.domain + '.' + key
  }

  static restore(key) {
    if (Browser.hasLocalstorage && localStorage[this._create_keyspace(key)]){
      return this._defaultConfig()[key]['parse'](localStorage[this._create_keyspace(key)])
    }
    return this._defaultValueFor(key)
  }

  static persist(key, value) {
    if (Browser.hasLocalstorage) {
      try {
        localStorage[this._create_keyspace(key)] = value
        return true
      } catch(e) {
        return false
      }
    }
  }
}

export function seekStringToSeconds(url) {
  var parts = url.match(/t=([0-9]*)(&|\/|$)/);
  if (parts && parts.length > 0) {
    return parseInt(parts[1], 10);
  } else {
    var seconds = 0;
    var factor = {'h': 3600, 'm': 60, 's': 1};
    parts = url.match(/[0-9]+[hms]+/g) || [];
    parts.forEach(function(el) {
      if (el) {
        var suffix = el[el.length - 1];
        var time = parseInt(el.slice(0, el.length - 1), 10);
        seconds += time * (factor[suffix]);
      }
    });
    return seconds;
   }
}

var idsCounter = {}

export function uniqueId(prefix) {
  idsCounter[prefix] || (idsCounter[prefix] = 0)
  var id = ++idsCounter[prefix]
  return prefix + id
}

export function isNumber(value) {
  return value - parseFloat(value) + 1 >= 0
}

export function currentScriptUrl() {
  var scripts = document.getElementsByTagName('script')
  return scripts[scripts.length - 1].src
}

export var requestAnimationFrame = (window.requestAnimationFrame ||
                            window.mozRequestAnimationFrame ||
                            window.webkitRequestAnimationFrame ||
                            function(fn) { window.setTimeout(fn, 1000/60) }).bind(window)

export var cancelAnimationFrame = (window.cancelAnimationFrame ||
                           window.mozCancelAnimationFrame ||
                           window.webkitCancelAnimationFrame ||
                           window.clearTimeout).bind(window)

export default {
  Config,
  Fullscreen,
  extend,
  formatTime,
  seekStringToSeconds,
  uniqueId,
  currentScriptUrl,
  isNumber,
  requestAnimationFrame,
  cancelAnimationFrame
}
<|endoftext|>


<commit_before>/**
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 23.06.18
 */
Imcms.define(
    'imcms-roles-tab-builder',
    [
        'imcms-super-admin-tab', 'imcms-i18n-texts', 'imcms-components-builder', 'imcms-roles-rest-api', 'imcms',
        'imcms-bem-builder', 'imcms-role-editor', 'jquery', 'imcms-role-to-row-transformer', 'imcms-authentication'
    ],
    function (SuperAdminTab, texts, components, rolesRestApi, imcms, BEM, roleEditor, $, roleToRow, auth) {

        texts = texts.superAdmin.roles;

        var $rolesContainer;

        function wrapInImcmsField($wrapMe) {
            return $('<div>', {
                'class': 'imcms-field',
                html: $wrapMe
            })
        }

        function buildTabTitle() {
            return wrapInImcmsField(components.texts.titleText('<div>', texts.title))
        }

        function onCreateNewRole() {
            $rolesContainer.find('.roles-table__role-row--active')
                .removeClass('roles-table__role-row--active');

            roleEditor.editRole($('<div>'), {
                id: null,
                name: '',
                permissions: {
                    getPasswordByEmail: false,
                    accessToAdminPages: false,
                    useImagesInImageArchive: false,
                    changeImagesInImageArchive: false
                }
            });
        }

        function buildCreateNewRoleButton() {
            return wrapInImcmsField(components.buttons.positiveButton({
                text: texts.createNewRole,
                click: onCreateNewRole
            }));
        }

        function buildRolesContainer() {
            $rolesContainer = $('<div>', {
                'class': 'roles-table'
            });

            rolesRestApi.read().success(function (roles) {
                $rolesContainer.append(roles.map(function (role) {
                    return roleToRow.transform(role, roleEditor)
                }))
            });

            return wrapInImcmsField([$rolesContainer, roleEditor.buildContainer()]);
        }

        function buildExternalRolesContainer() {
            var externalRolesBEM = new BEM({
                block: 'external-roles',
                elements: {
                    'auth-provider': ''
                }
            });

            var $externalRolesContainer = externalRolesBEM.buildBlock('<div>', [], {style: 'display: none;'});

            auth.getAuthProviders().success(function (providers) {
                if (!providers || !providers.length) return;

                $externalRolesContainer.css('display', 'block');

                var providerBEM = new BEM({
                    block: 'external-provider',
                    elements: {
                        'title': '',
                        'roles': ''
                    }
                });

                var providers$ = providers.map(function (provider) {

                    var $title = components.texts.titleText('<div>', provider.providerName).append($('<img>', {
                        'class': 'auth-provider-icon',
                        src: imcms.contextPath + provider.iconPath
                    }));
                    var $roles = $('<div>');
                    var $providerBlock = providerBEM.buildBlock('<div>', [
                        {'title': $title},
                        {'roles': $roles}
                    ]);
                    return externalRolesBEM.makeBlockElement('auth-provider', $providerBlock);
                });

                $externalRolesContainer.append(providers$);
            });

            return wrapInImcmsField($externalRolesContainer);
        }

        return new SuperAdminTab(texts.name, [
            buildTabTitle(),
            buildCreateNewRoleButton(),
            buildRolesContainer(),
            buildExternalRolesContainer()
        ]);
    }
);
<commit_msg>IMCMS-327 - New design to super admin page: roles tab: - Inserting external roles below local in super admin page.<commit_after>/**
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 23.06.18
 */
Imcms.define(
    'imcms-roles-tab-builder',
    [
        'imcms-super-admin-tab', 'imcms-i18n-texts', 'imcms-components-builder', 'imcms-roles-rest-api', 'imcms',
        'imcms-bem-builder', 'imcms-role-editor', 'jquery', 'imcms-role-to-row-transformer', 'imcms-authentication',
        'imcms-azure-roles-rest-api'
    ],
    function (SuperAdminTab, texts, components, rolesRestApi, imcms, BEM, roleEditor, $, roleToRow, auth, azureRoles) {

        texts = texts.superAdmin.roles;

        var $rolesContainer;

        function wrapInImcmsField($wrapMe) {
            return $('<div>', {
                'class': 'imcms-field',
                html: $wrapMe
            })
        }

        function buildTabTitle() {
            return wrapInImcmsField(components.texts.titleText('<div>', texts.title))
        }

        function onCreateNewRole() {
            $rolesContainer.find('.roles-table__role-row--active')
                .removeClass('roles-table__role-row--active');

            roleEditor.editRole($('<div>'), {
                id: null,
                name: '',
                permissions: {
                    getPasswordByEmail: false,
                    accessToAdminPages: false,
                    useImagesInImageArchive: false,
                    changeImagesInImageArchive: false
                }
            });
        }

        function buildCreateNewRoleButton() {
            return wrapInImcmsField(components.buttons.positiveButton({
                text: texts.createNewRole,
                click: onCreateNewRole
            }));
        }

        function buildRolesContainer() {
            $rolesContainer = $('<div>', {
                'class': 'roles-table'
            });

            rolesRestApi.read().success(function (roles) {
                $rolesContainer.append(roles.map(function (role) {
                    return roleToRow.transform(role, roleEditor)
                }))
            });

            return wrapInImcmsField([$rolesContainer, roleEditor.buildContainer()]);
        }

        function buildExternalRolesContainer() {
            var externalRolesBEM = new BEM({
                block: 'external-roles',
                elements: {
                    'auth-provider': ''
                }
            });

            var $externalRolesContainer = externalRolesBEM.buildBlock('<div>', [], {style: 'display: none;'});

            auth.getAuthProviders().success(function (providers) {
                if (!providers || !providers.length) return;

                $externalRolesContainer.css('display', 'block');

                var providerBEM = new BEM({
                    block: 'external-provider',
                    elements: {
                        'title': '',
                        'roles': ''
                    }
                });

                var providers$ = providers.map(function (provider) {
                    var $roles = $('<div>');

                    azureRoles.read().success(function (roles) {
                        var roles$ = roles.map(function (role) {
                            return $('<div>', {
                                text: role.displayName
                            });
                        });

                        $roles.append(roles$);
                    });

                    var $title = components.texts.titleText('<div>', provider.providerName).append($('<img>', {
                        'class': 'auth-provider-icon',
                        src: imcms.contextPath + provider.iconPath
                    }));
                    var $providerBlock = providerBEM.buildBlock('<div>', [
                        {'title': $title},
                        {'roles': $roles}
                    ]);
                    return externalRolesBEM.makeBlockElement('auth-provider', $providerBlock);
                });

                $externalRolesContainer.append(providers$);
            });

            return wrapInImcmsField($externalRolesContainer);
        }

        return new SuperAdminTab(texts.name, [
            buildTabTitle(),
            buildCreateNewRoleButton(),
            buildRolesContainer(),
            buildExternalRolesContainer()
        ]);
    }
);
<|endoftext|>


<commit_before>'use strict';

let styles = {

  overlay(isOpen) {
    return {
      position: 'fixed',
      zIndex: 1,
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      transform: isOpen ? '' : 'translate3d(-100%, 0, 0)',
      transition: isOpen ? 'opacity 0.3s' : 'opacity 0.3s, transform 0s 0.3s'
    };
  },

  menuWrap(isOpen, width, right) {
    return {
      position: 'fixed',
      right: right ? 0 : 'inherit',
      zIndex: 2,
      width,
      height: '100%',
      transform: isOpen ? '' : right ? 'translate3d(100%, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transition: 'all 0.5s'
    };
  },

  menu() {
    return {
      height: '100%',
      boxSizing: 'border-box'
    };
  },

  itemList() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  }

};

export default styles;
<commit_msg>Add overflow: auto to allow scrolling of menu items by default<commit_after>'use strict';

let styles = {

  overlay(isOpen) {
    return {
      position: 'fixed',
      zIndex: 1,
      width: '100%',
      height: '100%',
      background: 'rgba(0, 0, 0, 0.3)',
      opacity: isOpen ? 1 : 0,
      transform: isOpen ? '' : 'translate3d(-100%, 0, 0)',
      transition: isOpen ? 'opacity 0.3s' : 'opacity 0.3s, transform 0s 0.3s'
    };
  },

  menuWrap(isOpen, width, right) {
    return {
      position: 'fixed',
      right: right ? 0 : 'inherit',
      zIndex: 2,
      width,
      height: '100%',
      transform: isOpen ? '' : right ? 'translate3d(100%, 0, 0)' : 'translate3d(-100%, 0, 0)',
      transition: 'all 0.5s'
    };
  },

  menu() {
    return {
      height: '100%',
      boxSizing: 'border-box',
      overflow: 'auto'
    };
  },

  itemList() {
    return {
      height: '100%'
    };
  },

  item() {
    return {
      display: 'block',
      outline: 'none'
    };
  }

};

export default styles;
<|endoftext|>


<commit_before>(function (ng, $, page) {
	"use strict";

	/**
	 * Controller for the Project Samples Page.
	 * @param {Object} $scope Angular scope for this controller.
	 * @param {Object} $log Angular logging service.
	 * @param {Object} samplesService Service to handle server calls for samples.
	 * @param {Object} tableService Service to handle rendering the datatable.
	 * @constructor
	 */
	function SamplesController($scope, $log, modalService, samplesService, tableService) {
		var vm = this, previousIndex = null,
		    // Which projects to display
		    display = {
			    project: true,
			    local: []
		    };
		vm.samples = [];
		vm.selected = [];

		$scope.$on("DATATABLE_UPDATED", function () {
			previousIndex = null;
		});

		$scope.$watch("samplesCtrl.samples", function () {
			updateButtons();
		}, true);

		// BUTTON STATE
		vm.disabled = {
			lessThanTwo: true,
			lessThanOne: true,
			otherProjects: false
		};

		// Hide project name unless multiple displayed.
		vm.showProjectname = false;

		// Create the datatable.
		vm.dtColumnDefs = tableService.createTableColumnDefs();
		vm.dtOptions = tableService.createTableOptions();

		function displaySamples() {
			samplesService.fetchSamples(display).then(function (samples) {
				// Need to know if the sample should be selected, and remove any that are no longer in the table.;
				var s = [];
				samples.forEach(function (sample) {
					if (vm.selected.find(function (item) {
							// Check to see if the selected item matches the sample and from the right project.
							if (item.sample.identifier === sample.sample.identifier &&
								item.project.identifier === sample.project.identifier) {
								s.push(item);
								return true;
							}
						})) {
						sample.selected = true;
					}
				});
				// Update the samples that are selected and currently in the table.
				vm.selected = s;
				// Determine if the project name needs to be displayed in the table.
				vm.showProjectname = display.local.length > 0;
				// Update the samples;
				vm.samples = samples;
				updateButtons();
			});
		}

		vm.displayProjectsModal = function () {
			modalService.openAssociatedProjectsModal(display)
				.then(function (projectsToDisplay) {
					// Check to make sure their are updates to the table to process.
					if (!ng.equals(projectsToDisplay, display)) {
						display = projectsToDisplay;
						displaySamples();
					}
				})
		};

		vm.merge = function () {
			var ids = [];
			vm.selected.forEach(function (item) {
				ids.push(item.sample.identifier);
			});
			modalService.openMergeModal(vm.selected).then(function (result) {
				samplesService.mergeSamples(result).then(function () {
					// Need to reload the samples since the data has changed.
					samplesService.fetchSamples({showNotification: false}).then(function (samples) {
						vm.samples = samples;
						vm.selected = [];
					});
				});
			});
		};

		vm.copy = function () {
			modalService.openCopyModal(vm.selected).then(function (result) {
				samplesService.copySamples(result).then(function () {
					// No need to reload since it was only a copy
					vm.selected = [];
				});
			});
		};

		vm.move = function () {
			$log.warn("TODO: Implement move functionality");
		};

		vm.delete = function () {
			modalService.openRemoveModal(vm.selected).then(function () {
				samplesService.removeSamples(vm.selected).then(function () {
					vm.samples = vm.samples.filter(function (sample) {
						return !sample.selected;
					});
					vm.selected = [];
				});
			});

		};

		vm.addToCart = function () {
			var selected = vm.samples.filter(function (sample) {
				return sample.selected;
			});
			samplesService.addSamplesToCart(selected);
		};

		// This properly adds the buttons to the table.
		vm.dtInstanceCallback = function(instance) {
			tableService.initTable($scope, instance);
		};

		/**
		 * Responsible for selecting all or none of the samples
		 */
		vm.selectAll = function($event) {
			$event.stopPropagation();
			vm.selected = [];
			vm.samples.forEach(function (sample) {
				sample.selected = vm.allSelected;
				if(vm.allSelected) {
					vm.selected.push(sample)};
			});
		};

		/**
		 * Handles user clicking the datatable row.  Updates selected samples
		 * @param $event
		 * @param item
		 */
		vm.rowClick = function($event, $index) {
			$event.stopPropagation();
			var item = vm.samples[$index];

			// Start by selecting or deselecting the item
			if(item.selected) {
				vm.selected.push(item);
			}
			else {
				vm.selected.splice(vm.selected.indexOf(item), 1);
			}

			// Check for multiple selection
			if (!item.selected) {
				// This would be a deselection, and would result in no further actions.
				previousIndex = null;
			} else if (previousIndex !== null && $event.shiftKey) {
				// Multi-select here
				// Get the table rows
				var found = false;
				ng.element('tbody tr').each(function (i, row) {
					var rowIndex = ng.element(row).data("index"),
						rowItem = vm.samples[rowIndex];

					// Check to see if it was the previous clicked row or the currently clicked row.
					// This will mark the beginning or end of the selections.
					if(rowIndex === $index || rowItem === previousIndex) { found = !found; }
					if(found && !rowItem.selected) {
						rowItem.selected = true;
						vm.selected.push(rowItem);
					}
				});
				updateButtons();
			} else {
				previousIndex = item;
			}

			updateButtons();
		};

		/**
		 * Selection of all samples on the current page.
		 */
		vm.selectPage = function () {
			var rows = ng.element("tbody tr");
			rows.each(function(i, row) {
				var index = $(row).data("index"),
					item = vm.samples[index];
				if(!item.selected) {
					item.selected = true;
					vm.selected.push(item);
				}
			});
		};

		/**
		 * Determine how many samples are selected and update the buttons.
		 */
		function updateButtons(){
			var count = vm.selected.length;
			vm.allSelected = vm.samples.length === count;
			vm.disabled = {
				lessThanTwo: count < 2,
				lessThanOne: count < 1,
				otherProjects: display.local.length > 0
			};
		}

		// Load data into the table;
		displaySamples();
	}

	ng.module("irida.projects.samples.controller", ["irida.projects.samples.service", "irida.projects.samples.modals", "ngMessages", "ui.bootstrap"])
		.controller("SamplesController", ["$scope", "$log", "modalService",  "SamplesService", "TableService", SamplesController])
	;
})(window.angular, window.jQuery, window.PAGE);
<commit_msg>Clear the selected samples after copy<commit_after>(function (ng, $, page) {
	"use strict";

	/**
	 * Controller for the Project Samples Page.
	 * @param {Object} $scope Angular scope for this controller.
	 * @param {Object} $log Angular logging service.
	 * @param {Object} samplesService Service to handle server calls for samples.
	 * @param {Object} tableService Service to handle rendering the datatable.
	 * @constructor
	 */
	function SamplesController($scope, $log, modalService, samplesService, tableService) {
		var vm = this, previousIndex = null,
		    // Which projects to display
		    display = {
			    project: true,
			    local: []
		    };
		vm.samples = [];
		vm.selected = [];

		$scope.$on("DATATABLE_UPDATED", function () {
			previousIndex = null;
		});

		$scope.$watch("samplesCtrl.samples", function () {
			updateButtons();
		}, true);

		// BUTTON STATE
		vm.disabled = {
			lessThanTwo: true,
			lessThanOne: true,
			otherProjects: false
		};

		// Hide project name unless multiple displayed.
		vm.showProjectname = false;

		// Create the datatable.
		vm.dtColumnDefs = tableService.createTableColumnDefs();
		vm.dtOptions = tableService.createTableOptions();

		function displaySamples() {
			samplesService.fetchSamples(display).then(function (samples) {
				// Need to know if the sample should be selected, and remove any that are no longer in the table.;
				var s = [];
				samples.forEach(function (sample) {
					if (vm.selected.find(function (item) {
							// Check to see if the selected item matches the sample and from the right project.
							if (item.sample.identifier === sample.sample.identifier &&
								item.project.identifier === sample.project.identifier) {
								s.push(item);
								return true;
							}
						})) {
						sample.selected = true;
					}
				});
				// Update the samples that are selected and currently in the table.
				vm.selected = s;
				// Determine if the project name needs to be displayed in the table.
				vm.showProjectname = display.local.length > 0;
				// Update the samples;
				vm.samples = samples;
				updateButtons();
			});
		}

		vm.displayProjectsModal = function () {
			modalService.openAssociatedProjectsModal(display)
				.then(function (projectsToDisplay) {
					// Check to make sure their are updates to the table to process.
					if (!ng.equals(projectsToDisplay, display)) {
						display = projectsToDisplay;
						displaySamples();
					}
				})
		};

		vm.merge = function () {
			var ids = [];
			vm.selected.forEach(function (item) {
				ids.push(item.sample.identifier);
			});
			modalService.openMergeModal(vm.selected).then(function (result) {
				samplesService.mergeSamples(result).then(function () {
					// Need to reload the samples since the data has changed.
					samplesService.fetchSamples({showNotification: false}).then(function (samples) {
						vm.samples = samples;
						vm.selected = [];
					});
				});
			});
		};

		vm.copy = function () {
			modalService.openCopyModal(vm.selected).then(function (result) {
				samplesService.copySamples(result).then(function () {
					// No need to reload since it was only a copy
					// Just clear the selected ones.
					vm.selected.forEach(function (i) {
						i.selected = false;
					});
					vm.selected = [];
				});
			});
		};

		vm.move = function () {
			$log.warn("TODO: Implement move functionality");
		};

		vm.delete = function () {
			modalService.openRemoveModal(vm.selected).then(function () {
				samplesService.removeSamples(vm.selected).then(function () {
					vm.samples = vm.samples.filter(function (sample) {
						return !sample.selected;
					});
					vm.selected = [];
				});
			});

		};

		vm.addToCart = function () {
			var selected = vm.samples.filter(function (sample) {
				return sample.selected;
			});
			samplesService.addSamplesToCart(selected);
		};

		// This properly adds the buttons to the table.
		vm.dtInstanceCallback = function(instance) {
			tableService.initTable($scope, instance);
		};

		/**
		 * Responsible for selecting all or none of the samples
		 */
		vm.selectAll = function($event) {
			$event.stopPropagation();
			vm.selected = [];
			vm.samples.forEach(function (sample) {
				sample.selected = vm.allSelected;
				if(vm.allSelected) {
					vm.selected.push(sample)};
			});
		};

		/**
		 * Handles user clicking the datatable row.  Updates selected samples
		 * @param $event
		 * @param item
		 */
		vm.rowClick = function($event, $index) {
			$event.stopPropagation();
			var item = vm.samples[$index];

			// Start by selecting or deselecting the item
			if(item.selected) {
				vm.selected.push(item);
			}
			else {
				vm.selected.splice(vm.selected.indexOf(item), 1);
			}

			// Check for multiple selection
			if (!item.selected) {
				// This would be a deselection, and would result in no further actions.
				previousIndex = null;
			} else if (previousIndex !== null && $event.shiftKey) {
				// Multi-select here
				// Get the table rows
				var found = false;
				ng.element('tbody tr').each(function (i, row) {
					var rowIndex = ng.element(row).data("index"),
						rowItem = vm.samples[rowIndex];

					// Check to see if it was the previous clicked row or the currently clicked row.
					// This will mark the beginning or end of the selections.
					if(rowIndex === $index || rowItem === previousIndex) { found = !found; }
					if(found && !rowItem.selected) {
						rowItem.selected = true;
						vm.selected.push(rowItem);
					}
				});
				updateButtons();
			} else {
				previousIndex = item;
			}

			updateButtons();
		};

		/**
		 * Selection of all samples on the current page.
		 */
		vm.selectPage = function () {
			var rows = ng.element("tbody tr");
			rows.each(function(i, row) {
				var index = $(row).data("index"),
					item = vm.samples[index];
				if(!item.selected) {
					item.selected = true;
					vm.selected.push(item);
				}
			});
		};

		/**
		 * Determine how many samples are selected and update the buttons.
		 */
		function updateButtons(){
			var count = vm.selected.length;
			vm.allSelected = vm.samples.length === count;
			vm.disabled = {
				lessThanTwo: count < 2,
				lessThanOne: count < 1,
				otherProjects: display.local.length > 0
			};
		}

		// Load data into the table;
		displaySamples();
	}

	ng.module("irida.projects.samples.controller", ["irida.projects.samples.service", "irida.projects.samples.modals", "ngMessages", "ui.bootstrap"])
		.controller("SamplesController", ["$scope", "$log", "modalService",  "SamplesService", "TableService", SamplesController])
	;
})(window.angular, window.jQuery, window.PAGE);
<|endoftext|>