<commit_before>function (user, context, callback) {
  var namespace = 'https://sso.mozilla.com/claim/';
  var whitelist = ['']; // claim whitelist

  // If you're not OIDC Conformant, stop right there as this INCREASES the profile size
  // significantly! The metadata is set by us, manually.

  if (!context.clientMetadata || !context.clientMetadata.oidc_conformant || context.clientMetadata.oidc_conformant !== 'true') {
    console.log('Client '+context.clientID+' is not OIDC conformant yet, please fix!');
    // Bare minimum conversion to help migrating clients to OIDC conformant
    //context.idToken = context.idToken || {};
    //context.idToken[namespace+'groups'] = user.groups;
    return callback(null, user, context);
  }

  // CIS is our only functionality that can set app_metadata
  // Auth0 auto-integrate user.app_metadata.* into user.*
  // This rule cleans up the left overs for conciseness, clarity and size
  // (in particular this affects the id_token size, that goes onto GET queries
  // which are limited to 8k-if-not-less depending on the httpd server
  // settings)
  // Additional integration into user.app_metadata itself is at:
  // https://github.com/mozilla-iam/cis_functions/tree/master/functions/idvtoauth0

  // Fixup claims to be namespaced, for compat
  // Auth0 in the "OIDC-Conformant" mode will only allow these claims. That mode
  // enforces more than the OIDC spec in at least one regard:
  // Auth0 forbids the use of optional claims UNLESS these are namespaced by a URL.
  // See also https://auth0.com/docs/api-auth/tutorials/adoption/scope-custom-claims

  // These claims can be used directly and/or are preserved if integrated by Auth0
  // See also: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
  var std_oidc_claims = [
    "sub",
    "name",
    "given_name",
    "family_name",
    "middle_name",
    "nickname",
    "preferred_username",
    "profile",
    "picture",
    "website",
    "email",
    "email_verified",
    "gender",
    "birthdate",
    "zoneinfo",
    "locale",
    "phone_number",
    "phone_number_verified",
    "address",
    "updated_at"
    ];

  // These are the claims Auth0 used to provide, plus some of ours
  var old_authzero_claims = [
    "groups",
    // We don't pass these to reduce the profile size. use the CIS claim instead, or the CIS API
    //"emails",
    //"dn",
    //"organizationUnits",
    //"email_aliases",
    //"_HRData",
    // This is "sub"
    // "user_id"
    // We don't use these anyway:
    //"identities"
    //"multifactor"
    //"clientID"
    //"created_at"
  ];

  // XXX This code is no longer required once we get an LDAP identity driver
  // It reintegrates LDAP groups into the user's app_metadata/profile
  if (user.app_metadata !== undefined && user.app_metadata.length > 0) {

    // Force re-integrate LDAP groups until we have a LDAP CIS publisher
    // See https://github.com/mozilla-iam/cis_functions/blob/master/functions/idvtoauth0/main.py#L87
    // which this very code below supersedes as idvauth0 code only gets triggered if a profile is being actively published
    var index = 0;
    var userApiUrl = auth0.baseUrl + '/users/';
    var ugroups = user.app_metadata.groups;

    request({
      url: userApiUrl+user.user_id,
      headers: {
        Authorization: 'Bearer ' + auth0.accessToken
      },
    },
    function(err, response, body) {
      if (!err) {
        var udata = JSON.parse(body);
        // If user has no LDAP groups, return immediately
        if (udata.groups === undefined) {
          return cb();
        }

        // Add non-LDAP groups to new_groups
        var new_groups = [];

        for (index = 0; index < ugroups.length; ++index) {
          var cur_grp = ugroups[index];
          if (cur_grp === null) {
            console.log("CIS: Group data contained null entries in array, that's not normal");
            continue;
          }
          // Don't remove non-LDAP groups
          if (cur_grp.substr(0,14) === 'mozilliansorg_') {
            new_groups.push(cur_grp);
          } else if (cur_grp.substr(0,5) === 'hris_') {
            new_groups.push(cur_grp);
          }
        }

        // Re-add groups that are in LDAP (ie udata.groups)
        for (index = 0; index < udata.groups.length; ++index) {
            new_groups.push(udata.groups[index]);
        }
        return cb(new_groups);
      } else {
        console.log('CIS: Error while parsing user groups for user '+user.user_id+': '+err);
        return cb();
      }
      console.log('CIS: HTTP Error while parsing user groups for user '+user.user_id+': '+err);
      return cb();
    });
  } else {
    return cb();
  }

  // Ensure this is always called or the webtask will timeout
  function cb(new_groups) {
    user.app_metadata = user.app_metadata || {};
    if (new_groups !== undefined) {
      user.groups = new_groups;
      user.app_metadata.groups = new_groups;
      // Save app_metadata changes
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .catch(function(err){
          console.log('CIS: Error updating app_metadata (groups) for user '+user.user_id+': '+err);
      });
    }
    // XXX NOTE WARNING XXX
    // Do NOT enable this as this will inflate the id_token
    // This feature may only be used for specific scope or endpoints, whenever auth0 let us do that
    // Import entire CIS profile to a namespaced claim
    //context.idToken[namespace+'cis'] = user.app_metadata;

    // Reduce profile size further
    user.app_metadata = undefined;
    user.email_aliases = undefined;
    user.dn = undefined;
    user.organizationUnits = undefined;

    // Re-map old and new profile claims to namespaced claims
    // Basically that's `groups`
    old_authzero_claims.forEach(function(claim) {
      try {
        if (whitelist.indexOf(context.clientID) < 0) {
          context.idToken[namespace+claim] = user[claim];
        }
      } catch (e) {
        console.log("Undefined claim (non-fatal): "+e);
      }
    });

    // AAI value
    user.aai = user.aai || [];
    context.idToken[namespace+'AAI'] = user.aai;

    // Give info about CIS API
    context.idToken[namespace+'README_FIRST'] = 'Please refer to https://github.com/mozilla-iam/person-api in order to query Mozilla IAM CIS user profile data';
    return callback(null, user, context);
  }
}
<commit_msg>Set a new claim https://sso.mozilla.com/claim/original_connection_user_id<commit_after>function (user, context, callback) {
  var namespace = 'https://sso.mozilla.com/claim/';
  var whitelist = ['']; // claim whitelist

  // If you're not OIDC Conformant, stop right there as this INCREASES the profile size
  // significantly! The metadata is set by us, manually.

  if (!context.clientMetadata || !context.clientMetadata.oidc_conformant || context.clientMetadata.oidc_conformant !== 'true') {
    console.log('Client '+context.clientID+' is not OIDC conformant yet, please fix!');
    // Bare minimum conversion to help migrating clients to OIDC conformant
    //context.idToken = context.idToken || {};
    //context.idToken[namespace+'groups'] = user.groups;
    return callback(null, user, context);
  }

  // CIS is our only functionality that can set app_metadata
  // Auth0 auto-integrate user.app_metadata.* into user.*
  // This rule cleans up the left overs for conciseness, clarity and size
  // (in particular this affects the id_token size, that goes onto GET queries
  // which are limited to 8k-if-not-less depending on the httpd server
  // settings)
  // Additional integration into user.app_metadata itself is at:
  // https://github.com/mozilla-iam/cis_functions/tree/master/functions/idvtoauth0

  // Fixup claims to be namespaced, for compat
  // Auth0 in the "OIDC-Conformant" mode will only allow these claims. That mode
  // enforces more than the OIDC spec in at least one regard:
  // Auth0 forbids the use of optional claims UNLESS these are namespaced by a URL.
  // See also https://auth0.com/docs/api-auth/tutorials/adoption/scope-custom-claims

  // These claims can be used directly and/or are preserved if integrated by Auth0
  // See also: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
  var std_oidc_claims = [
    "sub",
    "name",
    "given_name",
    "family_name",
    "middle_name",
    "nickname",
    "preferred_username",
    "profile",
    "picture",
    "website",
    "email",
    "email_verified",
    "gender",
    "birthdate",
    "zoneinfo",
    "locale",
    "phone_number",
    "phone_number_verified",
    "address",
    "updated_at"
    ];

  // These are the claims Auth0 used to provide, plus some of ours
  var old_authzero_claims = [
    "groups",
    // We don't pass these to reduce the profile size. use the CIS claim instead, or the CIS API
    //"emails",
    //"dn",
    //"organizationUnits",
    //"email_aliases",
    //"_HRData",
    // This is "sub"
    // "user_id"
    // We don't use these anyway:
    //"identities"
    //"multifactor"
    //"clientID"
    //"created_at"
  ];

  // XXX This code is no longer required once we get an LDAP identity driver
  // It reintegrates LDAP groups into the user's app_metadata/profile
  if (user.app_metadata !== undefined && user.app_metadata.length > 0) {

    // Force re-integrate LDAP groups until we have a LDAP CIS publisher
    // See https://github.com/mozilla-iam/cis_functions/blob/master/functions/idvtoauth0/main.py#L87
    // which this very code below supersedes as idvauth0 code only gets triggered if a profile is being actively published
    var index = 0;
    var userApiUrl = auth0.baseUrl + '/users/';
    var ugroups = user.app_metadata.groups;

    request({
      url: userApiUrl+user.user_id,
      headers: {
        Authorization: 'Bearer ' + auth0.accessToken
      },
    },
    function(err, response, body) {
      if (!err) {
        var udata = JSON.parse(body);
        // If user has no LDAP groups, return immediately
        if (udata.groups === undefined) {
          return cb();
        }

        // Add non-LDAP groups to new_groups
        var new_groups = [];

        for (index = 0; index < ugroups.length; ++index) {
          var cur_grp = ugroups[index];
          if (cur_grp === null) {
            console.log("CIS: Group data contained null entries in array, that's not normal");
            continue;
          }
          // Don't remove non-LDAP groups
          if (cur_grp.substr(0,14) === 'mozilliansorg_') {
            new_groups.push(cur_grp);
          } else if (cur_grp.substr(0,5) === 'hris_') {
            new_groups.push(cur_grp);
          }
        }

        // Re-add groups that are in LDAP (ie udata.groups)
        for (index = 0; index < udata.groups.length; ++index) {
            new_groups.push(udata.groups[index]);
        }
        return cb(new_groups);
      } else {
        console.log('CIS: Error while parsing user groups for user '+user.user_id+': '+err);
        return cb();
      }
      console.log('CIS: HTTP Error while parsing user groups for user '+user.user_id+': '+err);
      return cb();
    });
  } else {
    return cb();
  }

  // Ensure this is always called or the webtask will timeout
  function cb(new_groups) {
    user.app_metadata = user.app_metadata || {};
    if (new_groups !== undefined) {
      user.groups = new_groups;
      user.app_metadata.groups = new_groups;
      // Save app_metadata changes
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .catch(function(err){
          console.log('CIS: Error updating app_metadata (groups) for user '+user.user_id+': '+err);
      });
    }
    // XXX NOTE WARNING XXX
    // Do NOT enable this as this will inflate the id_token
    // This feature may only be used for specific scope or endpoints, whenever auth0 let us do that
    // Import entire CIS profile to a namespaced claim
    //context.idToken[namespace+'cis'] = user.app_metadata;

    // Reduce profile size further
    user.app_metadata = undefined;
    user.email_aliases = undefined;
    user.dn = undefined;
    user.organizationUnits = undefined;

    // Re-map old and new profile claims to namespaced claims
    // Basically that's `groups`
    old_authzero_claims.forEach(function(claim) {
      try {
        if (whitelist.indexOf(context.clientID) < 0) {
          context.idToken[namespace+claim] = user[claim];
        }
      } catch (e) {
        console.log("Undefined claim (non-fatal): "+e);
      }
    });

    // AAI value
    user.aai = user.aai || [];
    context.idToken[namespace+'AAI'] = user.aai;

    // Original connection method's user_id (useful when the account is a linked account, this lets you know what the actual IdP
    // was used to login
    // Default to current user_id
    var originalConnection_user_id = user.user_id;
    var targetIdentity;
    // If we have linked account, check if we have a better match
    if (user.identities && user.identities.length > 1) {
      for (var i = 0; i < user.identities.length; i++) {
        targetIdentity = user.identities[i];
        // Find the identity which corresponding to the user logging in
        if ((targetIdentity.connection === context.connection) && (targetIdentity.provider === context.connectionStrategy)) {
          // If what we find has no `profileData` structure it means the user_id is the same as the one currently
          // logging in, so we don't need to do anything.
          // If it is, then we need to reconstruct a user_id from the identity data
          if (targetIdentity.profileData !== undefined) {
            originalConnection_user_id = targetIdentity.provider + '|' + targetIdentity.user_id;
          }
          break;
        }
      }
    }
    context.idToken[namespace+'original_connection_user_id'] = originalConnection_user_id;

    // Give info about CIS API
    context.idToken[namespace+'README_FIRST'] = 'Please refer to https://github.com/mozilla-iam/person-api in order to query Mozilla IAM CIS user profile data';
    return callback(null, user, context);
  }
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name	FB Cleanup
// @include	https://www.facebook.com/*
// //@run-at document-end
// @version		1.0.4
// @grant		none
// ==/UserScript==

// Library
var waitFor = function ( locator, callBack ) {

	if ( typeof locator === 'string' ) {
		var check = document.querySelector( locator );

		if ( check ) {
			callBack( check );

		} else {
			setTimeout( function () {

				waitFor( locator, callBack );
			}, 1 );
		}
	}
};

if ( window.top === window.self ) {
	var FB      = {};
	var options = {
		'suggestedPosts'   : false,
		'sponsoredPosts'   : false,
		'likedPosts'       : false,
		'commentedOnPosts' : false,
		'repliedToPosts'   : false,
		'wasTaggedInPosts' : false,
		'sharedPosts'      : false,
		'viaPosts'         : false,
		'appSuggestions'   : false,
		'trending'         : false,
		'suggestedPages'   : false
	};

	FB.suggestedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'Suggested Post';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed sponsored post.' );
					target.style.display = 'none';
				}

			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i = 0; i < tags.length; i++ ) {

			if ( tags[ i ].textContent == searchText ) {
				loopBack( tags[ i ] );
			}
		}
	};

	FB.sponsoredPosts = function () {

		var tags       = document.getElementsByTagName( 'a' );
		var searchText = 'Sponsored';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed sponsored post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i = 0; i < tags.length; i++ ) {

			if ( tags[ i ].textContent == searchText ) {
				loopBack( tags[ i ] );
			}
		}
	};

	FB.likedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'Sponsored';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed liked post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( 'liked this' ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.commentedOnPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'commented on';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed commented on post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.repliedToPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'replied to';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed replied to post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.wasTaggedInPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' was tagged in ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed was tagged in post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.sharedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' shared ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed shared post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.viaPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' via ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed via post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.appSuggestions = function ( event ) {

		if ( event.type === 'load' ) {
			event.element.remove();
			document.getElementsByClassName( 'fbChatSidebarBody' )[ 0 ].style.height = '100%';
		}
	};

	FB.trending = function ( event ) {

		if ( event.type === 'load' ) {
			document.getElementById( 'pagelet_trending_tags_and_topics' ).remove();
		}
	};

	FB.suggestedPages = function ( event ) {

		if ( event.type === 'load' ) {
			document.getElementById( 'pagelet_ego_pane' ).remove();
		}
	};

	for ( var func in FB ) {

		if ( !options[ func ] ) {
			window.addEventListener( 'scroll', FB[ func ].bind( null, 'scroll' ) );
		}
	}

	waitFor( '#pagelet_canvas_nav_content', function ( element ) {

		for ( var func in FB ) {

			if ( !options[ func ] ) {
				FB[ func ]( {
					'element' : element,
					'type'    : 'load'
				} );
			}
		}
	} );
}
<commit_msg>Update: increment version<commit_after>// ==UserScript==
// @name	FB Cleanup
// @include	https://www.facebook.com/*
// //@run-at document-end
// @version		1.0.5
// @grant		none
// ==/UserScript==

// Library
var waitFor = function ( locator, callBack ) {

	if ( typeof locator === 'string' ) {
		var check = document.querySelector( locator );

		if ( check ) {
			callBack( check );

		} else {
			setTimeout( function () {

				waitFor( locator, callBack );
			}, 1 );
		}
	}
};

if ( window.top === window.self ) {
	var FB      = {};
	var options = {
		'suggestedPosts'   : false,
		'sponsoredPosts'   : false,
		'likedPosts'       : false,
		'commentedOnPosts' : false,
		'repliedToPosts'   : false,
		'wasTaggedInPosts' : false,
		'sharedPosts'      : false,
		'viaPosts'         : false,
		'appSuggestions'   : false,
		'trending'         : false,
		'suggestedPages'   : false
	};

	FB.suggestedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'Suggested Post';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed sponsored post.' );
					target.style.display = 'none';
				}

			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i = 0; i < tags.length; i++ ) {

			if ( tags[ i ].textContent == searchText ) {
				loopBack( tags[ i ] );
			}
		}
	};

	FB.sponsoredPosts = function () {

		var tags       = document.getElementsByTagName( 'a' );
		var searchText = 'Sponsored';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed sponsored post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i = 0; i < tags.length; i++ ) {

			if ( tags[ i ].textContent == searchText ) {
				loopBack( tags[ i ] );
			}
		}
	};

	FB.likedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'Sponsored';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed liked post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( 'liked this' ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.commentedOnPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'commented on';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed commented on post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.repliedToPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = 'replied to';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed replied to post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.wasTaggedInPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' was tagged in ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed was tagged in post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.sharedPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' shared ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed shared post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.viaPosts = function () {

		var tags       = document.getElementsByTagName( 'span' );
		var searchText = ' via ';
		var loopBack   = function ( target ) {

			if ( getComputedStyle( target ).borderLeftStyle === 'solid' ) {

				if ( target.style.display !== 'none' ) {
					console.log( 'Removed via post.' );
					target.style.display = 'none';
				}
			} else if ( target.parentNode ) {
				loopBack( target.parentNode );
			}
		};

		for ( var i in tags ) {

			if ( tags[ i ].textContent
				&& tags[ i ].textContent.indexOf( searchText ) > -1
				&& getComputedStyle( tags[ i ] ).color.toString() == 'rgb(145, 151, 163)'
			) {
			    loopBack( tags[ i ] );
			}
		}
	};

	FB.appSuggestions = function ( event ) {

		if ( event.type === 'load' ) {
			event.element.remove();
			document.getElementsByClassName( 'fbChatSidebarBody' )[ 0 ].style.height = '100%';
		}
	};

	FB.trending = function ( event ) {

		if ( event.type === 'load' ) {
			document.getElementById( 'pagelet_trending_tags_and_topics' ).remove();
		}
	};

	FB.suggestedPages = function ( event ) {

		if ( event.type === 'load' ) {
			document.getElementById( 'pagelet_ego_pane' ).remove();
		}
	};

	for ( var func in FB ) {

		if ( !options[ func ] ) {
			window.addEventListener( 'scroll', FB[ func ].bind( null, 'scroll' ) );
		}
	}

	waitFor( '#pagelet_canvas_nav_content', function ( element ) {

		for ( var func in FB ) {

			if ( !options[ func ] ) {
				FB[ func ]( {
					'element' : element,
					'type'    : 'load'
				} );
			}
		}
	} );
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name	FB Cleanup
// @include	https://www.facebook.com/*
// //@run-at document-end
// @version		1.1.4
// @grant		none
// ==/UserScript==

( function FB_CleanUP () {

	var FB           = {};
	var util         = {};
	var queue        = {};
	var queueLast    = {};
	var queueTimeout = {};
	var options      = {
		'suggestedPosts'      : false,
		'sponsoredPosts'      : false,
		'likedPosts'          : false,
		'commentedOnPosts'    : false,
		'repliedToPosts'      : false,
		'wasTaggedInPosts'    : false,
		'sharedPosts'         : true,
		'viaPosts'            : true,
		'wasMentionedInPosts' : false,
		'wroteOnPosts'        : false,
		'gamesYouMayLike'     : false,
		'appSuggestions'      : false,
		'trending'            : false,
		'suggestedPages'      : false,
		'recommendedGames'    : false
	};

	FB.suggestedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent == 'Suggested Post' ) {
					util.removePost( element );
				}
			} );
		}
	};

	FB.sponsoredPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'a', function ( element ) {

				if ( element.textContent == 'Sponsored' ) {
					util.removePost( element );
				}
			} );
		};
	};

	FB.likedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( 'liked this' ) > -1 ) {

					util.removePost( element );
				}
			} );
		}
	};

	FB.commentedOnPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
					&& element.textContent.indexOf( 'commented on' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.repliedToPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( 'replied to' ) > -1 ) {

					element.style.backgroundColor = '000000';
					util.removePost( element );
				}
			});
		};
	};

	FB.taggedInPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' tagged in ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.sharedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' shared ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.viaPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' via ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.wasMentionedInPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' was mentioned in ' ) > -1 ) {

					util.removePost( element );
				}
			});
		};
	};

	FB.wroteOnPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' wrote on ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.gamesYouMayLike = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'div', function ( element ) {

				if ( element.textContent
					&& element.textContent.indexOf( 'See all game recommendations' ) > -1
					&& getComputedStyle( element ).color.toString() == 'rgb(145, 151, 163)'
				) {
					util.removePost( element );
				}
			} );
		};
	};

	FB.appSuggestions = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_canvas_nav_content', function ( element ) {

				element.remove();
				document.getElementsByClassName( 'fbChatSidebarBody' )[ 0 ].style.height = '100%';
			});
		}
	};

	FB.recommendedGames = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_games_rhc', function ( element ) {

				element.remove();
			});
		}
	};

	FB.trending = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_trending_tags_and_topics', function ( element ) {

				element.remove();
			} );
		};
	};

	FB.suggestedPages = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_ego_pane', function ( element ) {

				element.remove();
			} );
		};
	};

	util.waitFor = function ( locator, callBack ) {

		if ( typeof locator === 'string' ) {
			var check = document.querySelector( locator );

			if ( check ) {
				callBack( check );

			} else {
				setTimeout( function () {

					util.waitFor( locator, callBack );
				}, 1 );
			}
		}
	};

	util.find = function ( locator, callBack ) {

		if ( typeof locator === 'string' ) {
			var check = document.querySelector( locator );

			if ( check ) {
				callBack( check );

			}
		}
	};

	util.removePost = function ( target ) {

		if ( target.hasAttribute( 'data-cursor' ) ) {

			if ( target.style.display !== 'none' ) {
				target.style.display = 'none';
			}

		} else if ( target.parentNode ) {
			util.removePost( target.parentNode );
		}
	};

	util.filterElements = function ( element, callBack ) {

		if ( !queue[ element ] ) {
			queue[ element ] = [];
		}

		queue[ element ].push( callBack );

		clearTimeout( queueTimeout );

		queueTimeout = setTimeout( function () {

			for ( var type in queue ) {

				if ( queue.hasOwnProperty( type ) ) {
					var last;
					var contains;

					var tags = document.getElementsByTagName( type );

					if ( queueLast[ type ] ) {
						contains = tags.indexOf( queueLast[ type ] );
					}

					for ( var func in queue[ type ] ) {

						if ( queue[ type ].hasOwnProperty( func ) ) {

							for ( var i = ( ( contains > 0 ) ? contains : 0 ); i < tags.length; i++ ) {
								queue[ type ][ func ]( tags[ i ] );
							}

							last = tags[ i ];
						}
					}

					queueLast[ type ] = last;
				}
			}
		}, 1 );
	};

	if ( window.top === window.self ) {
		var active   = [];
		var observer = new MutationObserver( function ( mutations ) {

			for ( var i in active ) {

				if ( active.hasOwnProperty( i ) ) {
					active[ i ]();
				}
			}
		} );

		observer.observe( document.body, {
			'attributes'    : true,
			'childList'     : true,
			'characterData' : true
		} );

		for ( var func in FB ) {

			if ( FB.hasOwnProperty( func ) ) {

				if ( !options[ func ] ) {

					( function ( func ) {

						func.run();

						if ( func.events ) {

							for ( var i in func.events ) {

								if ( func.events.hasOwnProperty( i ) ) {

									if ( func.events[ i ] == 'change' ) {
										active.push( func.run );
									}
								}
							}
						}
					} )( new FB[ func ]() );
				}
			}
		}
	}
} )();
<commit_msg>Increment Version<commit_after>// ==UserScript==
// @name	FB Cleanup
// @include	https://www.facebook.com/*
// //@run-at document-end
// @version		1.1.5
// @grant		none
// ==/UserScript==

( function FB_CleanUP () {

	var FB           = {};
	var util         = {};
	var queue        = {};
	var queueLast    = {};
	var queueTimeout = {};
	var options      = {
		'suggestedPosts'      : false,
		'sponsoredPosts'      : false,
		'likedPosts'          : false,
		'commentedOnPosts'    : false,
		'repliedToPosts'      : false,
		'wasTaggedInPosts'    : false,
		'sharedPosts'         : true,
		'viaPosts'            : true,
		'wasMentionedInPosts' : false,
		'wroteOnPosts'        : false,
		'gamesYouMayLike'     : false,
		'appSuggestions'      : false,
		'trending'            : false,
		'suggestedPages'      : false,
		'recommendedGames'    : false
	};

	FB.suggestedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent == 'Suggested Post' ) {
					util.removePost( element );
				}
			} );
		}
	};

	FB.sponsoredPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'a', function ( element ) {

				if ( element.textContent == 'Sponsored' ) {
					util.removePost( element );
				}
			} );
		};
	};

	FB.likedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( 'liked this' ) > -1 ) {

					util.removePost( element );
				}
			} );
		}
	};

	FB.commentedOnPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
					&& element.textContent.indexOf( 'commented on' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.repliedToPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( 'replied to' ) > -1 ) {

					element.style.backgroundColor = '000000';
					util.removePost( element );
				}
			});
		};
	};

	FB.taggedInPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' tagged in ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.sharedPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' shared ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.viaPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' via ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.wasMentionedInPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' was mentioned in ' ) > -1 ) {

					util.removePost( element );
				}
			});
		};
	};

	FB.wroteOnPosts = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'span', function ( element ) {

				if ( element.textContent
				&&   element.textContent.indexOf( ' wrote on ' ) > -1 ) {

					util.removePost( element );
				}
			} );
		};
	};

	FB.gamesYouMayLike = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.filterElements( 'div', function ( element ) {

				if ( element.textContent
					&& element.textContent.indexOf( 'See all game recommendations' ) > -1
					&& getComputedStyle( element ).color.toString() == 'rgb(145, 151, 163)'
				) {
					util.removePost( element );
				}
			} );
		};
	};

	FB.appSuggestions = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_canvas_nav_content', function ( element ) {

				element.remove();
				document.getElementsByClassName( 'fbChatSidebarBody' )[ 0 ].style.height = '100%';
			});
		}
	};

	FB.recommendedGames = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_games_rhc', function ( element ) {

				element.remove();
			});
		}
	};

	FB.trending = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_trending_tags_and_topics', function ( element ) {

				element.remove();
			} );
		};
	};

	FB.suggestedPages = function () {

		this.events = [ 'change' ];
		this.run    = function () {

			util.find( '#pagelet_ego_pane', function ( element ) {

				element.remove();
			} );
		};
	};

	util.waitFor = function ( locator, callBack ) {

		if ( typeof locator === 'string' ) {
			var check = document.querySelector( locator );

			if ( check ) {
				callBack( check );

			} else {
				setTimeout( function () {

					util.waitFor( locator, callBack );
				}, 1 );
			}
		}
	};

	util.find = function ( locator, callBack ) {

		if ( typeof locator === 'string' ) {
			var check = document.querySelector( locator );

			if ( check ) {
				callBack( check );

			}
		}
	};

	util.removePost = function ( target ) {

		if ( target.hasAttribute( 'data-cursor' ) ) {

			if ( target.style.display !== 'none' ) {
				target.style.display = 'none';
			}

		} else if ( target.parentNode ) {
			util.removePost( target.parentNode );
		}
	};

	util.filterElements = function ( element, callBack ) {

		if ( !queue[ element ] ) {
			queue[ element ] = [];
		}

		queue[ element ].push( callBack );

		clearTimeout( queueTimeout );

		queueTimeout = setTimeout( function () {

			for ( var type in queue ) {

				if ( queue.hasOwnProperty( type ) ) {
					var last;
					var contains;

					var tags = document.getElementsByTagName( type );

					if ( queueLast[ type ] ) {
						contains = tags.indexOf( queueLast[ type ] );
					}

					for ( var func in queue[ type ] ) {

						if ( queue[ type ].hasOwnProperty( func ) ) {

							for ( var i = ( ( contains > 0 ) ? contains : 0 ); i < tags.length; i++ ) {
								queue[ type ][ func ]( tags[ i ] );
							}

							last = tags[ i ];
						}
					}

					queueLast[ type ] = last;
				}
			}
		}, 1 );
	};

	if ( window.top === window.self ) {
		var active   = [];
		var observer = new MutationObserver( function ( mutations ) {

			for ( var i in active ) {

				if ( active.hasOwnProperty( i ) ) {
					active[ i ]();
				}
			}
		} );

		observer.observe( document.body, {
			'attributes'    : true,
			'childList'     : true,
			'characterData' : true
		} );

		for ( var func in FB ) {

			if ( FB.hasOwnProperty( func ) ) {

				if ( !options[ func ] ) {

					( function ( func ) {

						func.run();

						if ( func.events ) {

							for ( var i in func.events ) {

								if ( func.events.hasOwnProperty( i ) ) {

									if ( func.events[ i ] == 'change' ) {
										active.push( func.run );
									}
								}
							}
						}
					} )( new FB[ func ]() );
				}
			}
		}
	}
} )();
<|endoftext|>


<commit_before>const electron = require('electron')
const fs = require('fs')
const path = require('path')
const app = electron.app // Module to control application life.
const protocol = electron.protocol // Module to control protocol handling
const BrowserWindow = electron.BrowserWindow // Module to create native browser window.
const webContents = electron.webContents
const session = electron.session
const ipc = electron.ipcMain

if (process.platform === 'win32') {
  (async function () {
  var squirrelCommand = process.argv[1];
  if (squirrelCommand === "--squirrel-install" || squirrelCommand === "--squirrel-updated") {
    await registryInstaller.install();
  }
  if (squirrelCommand == '--squirrel-uninstall') {
    await registryInstaller.uninstall();
  }
  if (require('electron-squirrel-startup')) {
    app.quit()
  }
  })();
}

// workaround for flicker when focusing app (https://github.com/electron/electron/issues/17942)
app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true')

var userDataPath = app.getPath('userData')

var dbPath = userDataPath + (process.platform === 'win32' ? '\\IndexedDB\\file__0.indexeddb.leveldb' : '/IndexedDB/file__0.indexeddb.leveldb')

if (process.argv.some(item => item.includes('rename-db'))) {
  try {
    fs.renameSync(dbPath, dbPath + '-' + Date.now() + '.recovery')
  } catch (e) {
    console.warn('renaming database failed', e)
    app.quit()
  }
}

const browserPage = 'file://' + __dirname + '/index.html'

var mainWindow = null
var mainMenu = null
var isFocusMode = false
var appIsReady = false

const isFirstInstance = app.requestSingleInstanceLock()

if (!isFirstInstance) {
  app.quit()
}

var saveWindowBounds = function () {
  if (mainWindow) {
    fs.writeFileSync(path.join(userDataPath, 'windowBounds.json'), JSON.stringify(mainWindow.getBounds()))
  }
}

function sendIPCToWindow (window, action, data) {
  // if there are no windows, create a new one
  if (!mainWindow) {
    createWindow(function () {
      mainWindow.webContents.send(action, data || {})
    })
  } else {
    mainWindow.webContents.send(action, data || {})
  }
}

function openTabInWindow (url) {
  sendIPCToWindow(mainWindow, 'addTab', {
    url: url
  })
}

function handleCommandLineArguments (argv) {
  // the "ready" event must occur before this function can be used
  if (argv) {
    argv.forEach(function (arg) {
      if (arg && arg.toLowerCase() !== __dirname.toLowerCase()) {
        if (arg.indexOf('://') !== -1) {
          sendIPCToWindow(mainWindow, 'addTab', {
            url: arg
          })
        } else if (/[A-Z]:[/\\].*\.html?$/.test(arg)) {
          //local files on Windows
          sendIPCToWindow(mainWindow, 'addTab', {
            url: "file://" + arg
          })
        }
      }
    })
  }
}

function createWindow (cb) {
  var savedBounds = fs.readFile(path.join(userDataPath, 'windowBounds.json'), 'utf-8', function (e, data) {
    var bounds

    if (data) {
      try {
        bounds = JSON.parse(data)
      } catch (e) {
        console.warn('error parsing window bounds file: ', e)
      }
    }
    if (e || !data || !bounds) { // there was an error, probably because the file doesn't exist
      var size = electron.screen.getPrimaryDisplay().workAreaSize
      var bounds = {
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
      }
    }

    // maximizes the window frame in windows 10
    // fixes https://github.com/minbrowser/min/issues/214
    // should be removed once https://github.com/electron/electron/issues/4045 is fixed
    if (process.platform === 'win32') {
      if (bounds.x === 0 || bounds.y === 0 || bounds.x === -8 || bounds.y === -8) {
        var screenSize = electron.screen.getPrimaryDisplay().workAreaSize
        if ((screenSize.width === bounds.width || bounds.width - screenSize.width === 16) && (screenSize.height === bounds.height || bounds.height - screenSize.height === 16)) {
          var shouldMaximize = true
        }
      }
    }

    createWindowWithBounds(bounds, shouldMaximize)

    if (cb) {
      cb()
    }
  })
}

function createWindowWithBounds (bounds, shouldMaximize) {
  mainWindow = new BrowserWindow({
    width: bounds.width,
    height: bounds.height,
    x: bounds.x,
    y: bounds.y,
    minWidth: (process.platform === 'win32' ? 400 : 320), // controls take up more horizontal space on Windows
    minHeight: 350,
    titleBarStyle: 'hiddenInset',
    icon: __dirname + '/icons/icon256.png',
    frame: process.platform !== 'win32',
    alwaysOnTop: settings.get('windowAlwaysOnTop'),
    backgroundColor: '#fff', // the value of this is ignored, but setting it seems to work around https://github.com/electron/electron/issues/10559
    webPreferences: {
      nodeIntegration: true,
      additionalArguments: ['--user-data-path=' + userDataPath]
    }
  })

  // and load the index.html of the app.
  mainWindow.loadURL(browserPage)

  if (shouldMaximize) {
    mainWindow.maximize()

    mainWindow.webContents.on('did-finish-load', function () {
      sendIPCToWindow(mainWindow, 'maximize')
    })
  }

  mainWindow.on('close', function () {
    destroyAllViews()
    // save the window size for the next launch of the app
    saveWindowBounds()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

  mainWindow.on('focus', function () {
    sendIPCToWindow(mainWindow, 'windowFocus')
  })

  mainWindow.on('minimize', function () {
    sendIPCToWindow(mainWindow, 'minimize')
  })

  mainWindow.on('maximize', function () {
    sendIPCToWindow(mainWindow, 'maximize')
  })

  mainWindow.on('unmaximize', function () {
    sendIPCToWindow(mainWindow, 'unmaximize')
  })

  mainWindow.on('enter-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-full-screen')
  })

  mainWindow.on('leave-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-full-screen')
  })

  mainWindow.on('enter-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-html-full-screen')
  })

  mainWindow.on('leave-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-html-full-screen')
  })

  mainWindow.on('app-command', function (e, command) {
    if (command === 'browser-backward') {
      sendIPCToWindow(mainWindow, 'goBack')
    } else if (command === 'browser-forward') {
      sendIPCToWindow(mainWindow, 'goForward')
    }
  })

  // prevent remote pages from being loaded using drag-and-drop, since they would have node access
  mainWindow.webContents.on('will-navigate', function (e, url) {
    if (url !== browserPage) {
      e.preventDefault()
    }
  })

  return mainWindow
}

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
  appIsReady = true

  createWindow(function () {
    mainWindow.webContents.on('did-finish-load', function () {
      // if a URL was passed as a command line argument (probably because Min is set as the default browser on Linux), open it.
      handleCommandLineArguments(process.argv)

      // there is a URL from an "open-url" event (on Mac)
      if (global.URLToOpen) {
        // if there is a previously set URL to open (probably from opening a link on macOS), open it
        sendIPCToWindow(mainWindow, 'addTab', {
          url: global.URLToOpen
        })
        global.URLToOpen = null
      }
    })
  })

  createAppMenu()
  createDockMenu()
  registerProtocols()
})

app.on('open-url', function (e, url) {
  if (appIsReady) {
    sendIPCToWindow(mainWindow, 'addTab', {
      url: url
    })
  } else {
    global.URLToOpen = url // this will be handled later in the createWindow callback
  }
})

app.on('second-instance', function (e, argv, workingDir) {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }
    mainWindow.focus()
    // add a tab with the new URL
    handleCommandLineArguments(argv)
  }
})

/**
 * Emitted when the application is activated, which usually happens when clicks on the applications's dock icon
 * https://github.com/electron/electron/blob/master/docs/api/app.md#event-activate-os-x
 *
 * Opens a new tab when all tabs are closed, and min is still open by clicking on the application dock icon
 */
app.on('activate', function (/* e, hasVisibleWindows */) {
  if (!mainWindow && appIsReady) { // sometimes, the event will be triggered before the app is ready, and creating new windows will fail
    createWindow()
  }
})

ipc.on('focusMainWebContents', function () {
  mainWindow.webContents.focus()
})

ipc.on('showSecondaryMenu', function (event, data) {
  if (mainMenu) {
    mainMenu.popup(mainWindow, {
      x: data.x,
      y: data.y,
      async: true
    })
  }
})

function registerProtocols () {
  protocol.registerStringProtocol('mailto', function (req, cb) {
    electron.shell.openExternal(req.url)
    return null
  })
}
<commit_msg>prevent app window from opening offscreen<commit_after>const electron = require('electron')
const fs = require('fs')
const path = require('path')
const app = electron.app // Module to control application life.
const protocol = electron.protocol // Module to control protocol handling
const BrowserWindow = electron.BrowserWindow // Module to create native browser window.
const webContents = electron.webContents
const session = electron.session
const ipc = electron.ipcMain

function clamp(n, min, max) {
  return Math.max(Math.min(n, max), min);
}

if (process.platform === 'win32') {
  (async function () {
  var squirrelCommand = process.argv[1];
  if (squirrelCommand === "--squirrel-install" || squirrelCommand === "--squirrel-updated") {
    await registryInstaller.install();
  }
  if (squirrelCommand == '--squirrel-uninstall') {
    await registryInstaller.uninstall();
  }
  if (require('electron-squirrel-startup')) {
    app.quit()
  }
  })();
}

// workaround for flicker when focusing app (https://github.com/electron/electron/issues/17942)
app.commandLine.appendSwitch('disable-backgrounding-occluded-windows', 'true')

var userDataPath = app.getPath('userData')

var dbPath = userDataPath + (process.platform === 'win32' ? '\\IndexedDB\\file__0.indexeddb.leveldb' : '/IndexedDB/file__0.indexeddb.leveldb')

if (process.argv.some(item => item.includes('rename-db'))) {
  try {
    fs.renameSync(dbPath, dbPath + '-' + Date.now() + '.recovery')
  } catch (e) {
    console.warn('renaming database failed', e)
    app.quit()
  }
}

const browserPage = 'file://' + __dirname + '/index.html'

var mainWindow = null
var mainMenu = null
var isFocusMode = false
var appIsReady = false

const isFirstInstance = app.requestSingleInstanceLock()

if (!isFirstInstance) {
  app.quit()
}

var saveWindowBounds = function () {
  if (mainWindow) {
    fs.writeFileSync(path.join(userDataPath, 'windowBounds.json'), JSON.stringify(mainWindow.getBounds()))
  }
}

function sendIPCToWindow (window, action, data) {
  // if there are no windows, create a new one
  if (!mainWindow) {
    createWindow(function () {
      mainWindow.webContents.send(action, data || {})
    })
  } else {
    mainWindow.webContents.send(action, data || {})
  }
}

function openTabInWindow (url) {
  sendIPCToWindow(mainWindow, 'addTab', {
    url: url
  })
}

function handleCommandLineArguments (argv) {
  // the "ready" event must occur before this function can be used
  if (argv) {
    argv.forEach(function (arg) {
      if (arg && arg.toLowerCase() !== __dirname.toLowerCase()) {
        if (arg.indexOf('://') !== -1) {
          sendIPCToWindow(mainWindow, 'addTab', {
            url: arg
          })
        } else if (/[A-Z]:[/\\].*\.html?$/.test(arg)) {
          //local files on Windows
          sendIPCToWindow(mainWindow, 'addTab', {
            url: "file://" + arg
          })
        }
      }
    })
  }
}

function createWindow (cb) {
  var savedBounds = fs.readFile(path.join(userDataPath, 'windowBounds.json'), 'utf-8', function (e, data) {
    var bounds

    if (data) {
      try {
        bounds = JSON.parse(data)
      } catch (e) {
        console.warn('error parsing window bounds file: ', e)
      }
    }
    if (e || !data || !bounds) { // there was an error, probably because the file doesn't exist
      var size = electron.screen.getPrimaryDisplay().workAreaSize
      bounds = {
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
      }
    }

    //make the bounds fit inside a currently-active screen
    //(since the screen Min was previously open on could have been removed)
    //see: https://github.com/minbrowser/min/issues/904
    var containingRect = electron.screen.getDisplayMatching(bounds).workArea;

    bounds = {
      x: clamp(bounds.x, containingRect.x, (containingRect.x + containingRect.width) - bounds.width),
      y: clamp(bounds.y, containingRect.y, (containingRect.y + containingRect.height) - bounds.height),
      width: clamp(bounds.width, 0, containingRect.width),
      height: clamp(bounds.height, 0, containingRect.height),
    }

    // maximizes the window frame in windows 10
    // fixes https://github.com/minbrowser/min/issues/214
    // should be removed once https://github.com/electron/electron/issues/4045 is fixed
    if (process.platform === 'win32') {
      if (bounds.x === 0 || bounds.y === 0 || bounds.x === -8 || bounds.y === -8) {
        var screenSize = electron.screen.getPrimaryDisplay().workAreaSize
        if ((screenSize.width === bounds.width || bounds.width - screenSize.width === 16) && (screenSize.height === bounds.height || bounds.height - screenSize.height === 16)) {
          var shouldMaximize = true
        }
      }
    }

    createWindowWithBounds(bounds, shouldMaximize)

    if (cb) {
      cb()
    }
  })
}

function createWindowWithBounds (bounds, shouldMaximize) {
  mainWindow = new BrowserWindow({
    width: bounds.width,
    height: bounds.height,
    x: bounds.x,
    y: bounds.y,
    minWidth: (process.platform === 'win32' ? 400 : 320), // controls take up more horizontal space on Windows
    minHeight: 350,
    titleBarStyle: 'hiddenInset',
    icon: __dirname + '/icons/icon256.png',
    frame: process.platform !== 'win32',
    alwaysOnTop: settings.get('windowAlwaysOnTop'),
    backgroundColor: '#fff', // the value of this is ignored, but setting it seems to work around https://github.com/electron/electron/issues/10559
    webPreferences: {
      nodeIntegration: true,
      additionalArguments: ['--user-data-path=' + userDataPath]
    }
  })

  // and load the index.html of the app.
  mainWindow.loadURL(browserPage)

  if (shouldMaximize) {
    mainWindow.maximize()

    mainWindow.webContents.on('did-finish-load', function () {
      sendIPCToWindow(mainWindow, 'maximize')
    })
  }

  mainWindow.on('close', function () {
    destroyAllViews()
    // save the window size for the next launch of the app
    saveWindowBounds()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

  mainWindow.on('focus', function () {
    sendIPCToWindow(mainWindow, 'windowFocus')
  })

  mainWindow.on('minimize', function () {
    sendIPCToWindow(mainWindow, 'minimize')
  })

  mainWindow.on('maximize', function () {
    sendIPCToWindow(mainWindow, 'maximize')
  })

  mainWindow.on('unmaximize', function () {
    sendIPCToWindow(mainWindow, 'unmaximize')
  })

  mainWindow.on('enter-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-full-screen')
  })

  mainWindow.on('leave-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-full-screen')
  })

  mainWindow.on('enter-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-html-full-screen')
  })

  mainWindow.on('leave-html-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-html-full-screen')
  })

  mainWindow.on('app-command', function (e, command) {
    if (command === 'browser-backward') {
      sendIPCToWindow(mainWindow, 'goBack')
    } else if (command === 'browser-forward') {
      sendIPCToWindow(mainWindow, 'goForward')
    }
  })

  // prevent remote pages from being loaded using drag-and-drop, since they would have node access
  mainWindow.webContents.on('will-navigate', function (e, url) {
    if (url !== browserPage) {
      e.preventDefault()
    }
  })

  return mainWindow
}

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
  appIsReady = true

  createWindow(function () {
    mainWindow.webContents.on('did-finish-load', function () {
      // if a URL was passed as a command line argument (probably because Min is set as the default browser on Linux), open it.
      handleCommandLineArguments(process.argv)

      // there is a URL from an "open-url" event (on Mac)
      if (global.URLToOpen) {
        // if there is a previously set URL to open (probably from opening a link on macOS), open it
        sendIPCToWindow(mainWindow, 'addTab', {
          url: global.URLToOpen
        })
        global.URLToOpen = null
      }
    })
  })

  createAppMenu()
  createDockMenu()
  registerProtocols()
})

app.on('open-url', function (e, url) {
  if (appIsReady) {
    sendIPCToWindow(mainWindow, 'addTab', {
      url: url
    })
  } else {
    global.URLToOpen = url // this will be handled later in the createWindow callback
  }
})

app.on('second-instance', function (e, argv, workingDir) {
  if (mainWindow) {
    if (mainWindow.isMinimized()) {
      mainWindow.restore()
    }
    mainWindow.focus()
    // add a tab with the new URL
    handleCommandLineArguments(argv)
  }
})

/**
 * Emitted when the application is activated, which usually happens when clicks on the applications's dock icon
 * https://github.com/electron/electron/blob/master/docs/api/app.md#event-activate-os-x
 *
 * Opens a new tab when all tabs are closed, and min is still open by clicking on the application dock icon
 */
app.on('activate', function (/* e, hasVisibleWindows */) {
  if (!mainWindow && appIsReady) { // sometimes, the event will be triggered before the app is ready, and creating new windows will fail
    createWindow()
  }
})

ipc.on('focusMainWebContents', function () {
  mainWindow.webContents.focus()
})

ipc.on('showSecondaryMenu', function (event, data) {
  if (mainMenu) {
    mainMenu.popup(mainWindow, {
      x: data.x,
      y: data.y,
      async: true
    })
  }
})

function registerProtocols () {
  protocol.registerStringProtocol('mailto', function (req, cb) {
    electron.shell.openExternal(req.url)
    return null
  })
}
<|endoftext|>


<commit_before>/************************************************************************
## Fireworks Module

The fireworks module makes it easy to create fireworks using
ScriptCraft.  The module has a single function `firework` which takes
a `org.bukkit.Location` as its 1 and only parameter.

### Examples

The module also extends the `Drone` object adding a `firework` method
so that fireworks can be created as a part of a Drone chain. For
Example....

    /js firework()

... creates a single firework, while ....

    /js firework().fwd(3).times(5)

... creates 5 fireworks in a row. Fireworks have also been added as a
possible option for the `arrow` module. To have a firework launch
where an arrow strikes...

    /js arrows.firework()

To call the fireworks.firework() function directly, you must provide a
location. For example...

    /js var fireworks = require('fireworks');
    /js fireworks.firework( self.location );

![firework example](img/firework.png)

***/

if ( __plugin.canary ) {
  exports.firework = require('./bukkit/fireworks');
} else {
  exports.firework = require('./canary/fireworks');
}

<commit_msg>fix bug in fireworks (was using wrong framework)<commit_after>/************************************************************************
## Fireworks Module

The fireworks module makes it easy to create fireworks using
ScriptCraft.  The module has a single function `firework` which takes
a `org.bukkit.Location` as its 1 and only parameter.

### Examples

The module also extends the `Drone` object adding a `firework` method
so that fireworks can be created as a part of a Drone chain. For
Example....

    /js firework()

... creates a single firework, while ....

    /js firework().fwd(3).times(5)

... creates 5 fireworks in a row. Fireworks have also been added as a
possible option for the `arrow` module. To have a firework launch
where an arrow strikes...

    /js arrows.firework()

To call the fireworks.firework() function directly, you must provide a
location. For example...

    /js var fireworks = require('fireworks');
    /js fireworks.firework( self.location );

![firework example](img/firework.png)

***/

if ( __plugin.canary ) {
  exports.firework = require('./canary/fireworks');
} else {
  exports.firework = require('./bukkit/fireworks');
}

<|endoftext|>


<commit_before>
kff.FrontController = kff.createClass(
{
	statics: {
		service: {
			args: [{
				viewFactory: '@kff.ViewFactory',
				defaultView: 'kff.PageView',
				stateHandler: '@kff.HashStateHandler',
				element: null
			}],
			shared: true
		}
	}
},
/** @lends kff.FrontController.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function(options)
	{
		options = options || {};
		this.options = options;
		this.views = null;
		this.viewsQueue = [];
		this.viewFactory = options.viewFactory;
		this.defaultView = options.defaultView;
		this.router = options.router || null;
		this.rootElement = options.element || null;
		this.stateHandler = options.stateHandler || null;
		this.middlewares = options.middlewares || [];
		this.dispatcher = options.dispatcher || null;
		this.env = options.env || { document: document, window: window };
	},

	/**
	 * Inits front controller
	 */
	init: function()
	{
		if(!this.viewFactory) this.viewFactory = new kff.ViewFactory();
		if(this.router && this.stateHandler)
		{
			this.stateHandler.on('popstate', this.f('setState'));
			this.stateHandler.init();
		}
		else this.setState(null);
	},

	/**
	 * Destroys front controller
	 */
	destroy: function()
	{
		var destroyQueue = [], lastViewName, i;
		while((lastViewName = this.getLastView() ? this.getLastView().name : null) !== null)
		{
			destroyQueue.push(this.popView());
		}

		for(i = 0; i < destroyQueue.length; i++)
		{
			if(destroyQueue[i + 1]) destroyQueue[i].instance.on('destroy', kff.bindFn(destroyQueue[i + 1].instance, 'destroyAll'));
			else destroyQueue[i].instance.on('destroy', kff.bindFn(this, 'destroyDone'));
		}

		if(destroyQueue[0]) destroyQueue[0].instance.destroyAll();
		else this.destroyDone();
	},

	/**
	 * Async callback for destroy method
	 *
	 * @private
	 */
	destroyDone: function()
	{
		if(this.router && this.stateHandler)
		{
			this.stateHandler.off('popstate', this.f('setState'));
			this.stateHandler.destroy();
		}
		if(this.viewFactory) this.viewFactory = null;
	},

	/**
	 * Constructs view name from state object
	 *
	 * @param  {object} state State object
	 * @return {string}       Name (service name) of the view
	 */
	createViewFromState: function(state)
	{
		var result = null, viewName = this.defaultView;
		if(this.router && this.state)
		{
			var path = state.path;

			if(path === '') path = '#';

			result = this.router.match(path);
			if(result)
			{
				state.params = result.params;
			}
		}
		if(result) viewName = result.target;

		viewName = this.processMiddlewares(viewName, state);

		return viewName;
	},

	/**
	 * Process/transforms view name by middleware functions
	 *
	 * @private
	 * @param  {string} viewName Service name of the view
	 * @param  {object} state    State object
	 * @return {string}          Transformed view name
	 */
	processMiddlewares: function(viewName, state)
	{
		for(var i = 0, l = this.middlewares.length; i < l; i++)
		{
			viewName = this.middlewares[i].call(null, viewName, state);
		}
		return viewName;
	},

	/**
	 * Returns last view metaobject in the views queue
	 *
	 * @private
	 * @return {object} Metaobject with the last view
	 */
	getLastView: function()
	{
		if(this.viewsQueue.length > 0) return this.viewsQueue[this.viewsQueue.length - 1];
		else return null;
	},

	/**
	 * Adds view metaobject to the queue
	 *
	 * @private
	 * @param  {object} view View metaobject
	 */
	pushView: function(view)
	{
		var lastView = this.getLastView();
		this.viewsQueue.push(view);
		if(lastView)
		{
			lastView.instance.on('render', kff.bindFn(view.instance, 'init'));
			lastView.instance.on('setState', kff.bindFn(view.instance, 'setState'));
		}
	},

	/**
	 * Returns, destroys and removes last view from the queue
	 * @return {object} View metaobject
	 */
	popView: function()
	{
		if(this.viewsQueue.length === 0) return;

		var removedView = this.viewsQueue.pop(),
			lastView = this.getLastView();

		removedView.instance.off('render', kff.bindFn(this, 'cascadeState'));
		if(lastView)
		{
			lastView.instance.off('render', kff.bindFn(removedView.instance, 'init'));
			lastView.instance.off('setState', kff.bindFn(removedView.instance, 'setState'));
		}
		return removedView;
	},

	cascadeState: function()
	{
		if(this.viewsQueue[0]) this.viewsQueue[0].instance.setState(this.state);
	},

	setState: function(state)
	{
		var destroyQueue = [], lastViewName, sharedViewName, i;

		this.state = state;
		this.newViewName = this.createViewFromState(state);
		lastViewName = this.getLastView() ? this.getLastView().name : null;
		sharedViewName = this.findSharedView(this.newViewName, lastViewName);

		while((lastViewName = this.getLastView() ? this.getLastView().name : null) !== null)
		{
			if(lastViewName === sharedViewName) break;
			destroyQueue.push(this.popView());
		}

		for(i = 0; i < destroyQueue.length; i++)
		{
			if(destroyQueue[i + 1]) destroyQueue[i].instance.on('destroy', kff.bindFn(destroyQueue[i + 1].instance, 'destroyAll'));
			else destroyQueue[i].instance.on('destroy', kff.bindFn(this, 'startInit'));
		}

		if(destroyQueue[0]) destroyQueue[0].instance.destroyAll();
		else this.startInit();

		if(this.dispatcher)
		{
			this.dispatcher.trigger('route', {
				state: state
			});
		}
	},

	startInit: function()
	{
		var i, l, view, options = {},
			precedingViewNames = this.getPrecedingViews(this.newViewName),
			from = 0;

		if(this.rootElement) options = { element: this.rootElement };
		if(this.dispatcher) options.dispatcher = this.dispatcher;
		options.env = this.env;

		for(i = 0, l = precedingViewNames.length; i < l; i++)
		{
			if(i >= this.viewsQueue.length)
			{
				view = this.viewFactory.createView(precedingViewNames[i], options);
				view.setViewFactory(this.viewFactory);
				this.pushView({ name: precedingViewNames[i], instance: view });
			}
			else from = i + 1;
		}

		this.newViewName = null;
		if(this.getLastView()) this.getLastView().instance.on('render', kff.bindFn(this, 'cascadeState'));
		if(this.viewsQueue[from]) this.viewsQueue[from].instance.init();
		else this.cascadeState();
	},

	findSharedView: function(c1, c2)
	{
		var i,
			c1a = this.getPrecedingViews(c1),
			c2a = this.getPrecedingViews(c2),
			c = null;

		for(i = 0, l = c1a.length < c2a.length ? c1a.length : c2a.length; i < l; i++)
		{
			if(c1a[i] !== c2a[i]) break;
			c = c1a[i];
		}
		return c;
	},

	getPrecedingViews: function(viewName)
	{
		var c = viewName, a = [c];

		while(c)
		{
			c = this.viewFactory.getPrecedingView(c);
			if(c) a.unshift(c);
		}
		return a;
	},

	getViewFactory: function()
	{
		return this.viewFactory;
	},

	setViewFactory: function(viewFactory)
	{
		this.viewFactory = viewFactory;
	},

	getRouter: function()
	{
		return this.router;
	},

	setRouter: function(router)
	{
		this.router = router;
	},

	setDefaultView: function(defaultView)
	{
		this.defaultView = defaultView;
	},

	setStateHandler: function(stateHandler)
	{
		this.stateHandler = stateHandler;
	},

	setDispatcher: function(dispatcher)
	{
		this.dispatcher = dispatcher;
	}
});
<commit_msg>fix(kff.FrontController#findSharedView): undeclared l variable<commit_after>
kff.FrontController = kff.createClass(
{
	statics: {
		service: {
			args: [{
				viewFactory: '@kff.ViewFactory',
				defaultView: 'kff.PageView',
				stateHandler: '@kff.HashStateHandler',
				element: null
			}],
			shared: true
		}
	}
},
/** @lends kff.FrontController.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function(options)
	{
		options = options || {};
		this.options = options;
		this.views = null;
		this.viewsQueue = [];
		this.viewFactory = options.viewFactory;
		this.defaultView = options.defaultView;
		this.router = options.router || null;
		this.rootElement = options.element || null;
		this.stateHandler = options.stateHandler || null;
		this.middlewares = options.middlewares || [];
		this.dispatcher = options.dispatcher || null;
		this.env = options.env || { document: document, window: window };
	},

	/**
	 * Inits front controller
	 */
	init: function()
	{
		if(!this.viewFactory) this.viewFactory = new kff.ViewFactory();
		if(this.router && this.stateHandler)
		{
			this.stateHandler.on('popstate', this.f('setState'));
			this.stateHandler.init();
		}
		else this.setState(null);
	},

	/**
	 * Destroys front controller
	 */
	destroy: function()
	{
		var destroyQueue = [], lastViewName, i;
		while((lastViewName = this.getLastView() ? this.getLastView().name : null) !== null)
		{
			destroyQueue.push(this.popView());
		}

		for(i = 0; i < destroyQueue.length; i++)
		{
			if(destroyQueue[i + 1]) destroyQueue[i].instance.on('destroy', kff.bindFn(destroyQueue[i + 1].instance, 'destroyAll'));
			else destroyQueue[i].instance.on('destroy', kff.bindFn(this, 'destroyDone'));
		}

		if(destroyQueue[0]) destroyQueue[0].instance.destroyAll();
		else this.destroyDone();
	},

	/**
	 * Async callback for destroy method
	 *
	 * @private
	 */
	destroyDone: function()
	{
		if(this.router && this.stateHandler)
		{
			this.stateHandler.off('popstate', this.f('setState'));
			this.stateHandler.destroy();
		}
		if(this.viewFactory) this.viewFactory = null;
	},

	/**
	 * Constructs view name from state object
	 *
	 * @param  {object} state State object
	 * @return {string}       Name (service name) of the view
	 */
	createViewFromState: function(state)
	{
		var result = null, viewName = this.defaultView;
		if(this.router && this.state)
		{
			var path = state.path;

			if(path === '') path = '#';

			result = this.router.match(path);
			if(result)
			{
				state.params = result.params;
			}
		}
		if(result) viewName = result.target;

		viewName = this.processMiddlewares(viewName, state);

		return viewName;
	},

	/**
	 * Process/transforms view name by middleware functions
	 *
	 * @private
	 * @param  {string} viewName Service name of the view
	 * @param  {object} state    State object
	 * @return {string}          Transformed view name
	 */
	processMiddlewares: function(viewName, state)
	{
		for(var i = 0, l = this.middlewares.length; i < l; i++)
		{
			viewName = this.middlewares[i].call(null, viewName, state);
		}
		return viewName;
	},

	/**
	 * Returns last view metaobject in the views queue
	 *
	 * @private
	 * @return {object} Metaobject with the last view
	 */
	getLastView: function()
	{
		if(this.viewsQueue.length > 0) return this.viewsQueue[this.viewsQueue.length - 1];
		else return null;
	},

	/**
	 * Adds view metaobject to the queue
	 *
	 * @private
	 * @param  {object} view View metaobject
	 */
	pushView: function(view)
	{
		var lastView = this.getLastView();
		this.viewsQueue.push(view);
		if(lastView)
		{
			lastView.instance.on('render', kff.bindFn(view.instance, 'init'));
			lastView.instance.on('setState', kff.bindFn(view.instance, 'setState'));
		}
	},

	/**
	 * Returns, destroys and removes last view from the queue
	 * @return {object} View metaobject
	 */
	popView: function()
	{
		if(this.viewsQueue.length === 0) return;

		var removedView = this.viewsQueue.pop(),
			lastView = this.getLastView();

		removedView.instance.off('render', kff.bindFn(this, 'cascadeState'));
		if(lastView)
		{
			lastView.instance.off('render', kff.bindFn(removedView.instance, 'init'));
			lastView.instance.off('setState', kff.bindFn(removedView.instance, 'setState'));
		}
		return removedView;
	},

	cascadeState: function()
	{
		if(this.viewsQueue[0]) this.viewsQueue[0].instance.setState(this.state);
	},

	setState: function(state)
	{
		var destroyQueue = [], lastViewName, sharedViewName, i;

		this.state = state;
		this.newViewName = this.createViewFromState(state);
		lastViewName = this.getLastView() ? this.getLastView().name : null;
		sharedViewName = this.findSharedView(this.newViewName, lastViewName);

		while((lastViewName = this.getLastView() ? this.getLastView().name : null) !== null)
		{
			if(lastViewName === sharedViewName) break;
			destroyQueue.push(this.popView());
		}

		for(i = 0; i < destroyQueue.length; i++)
		{
			if(destroyQueue[i + 1]) destroyQueue[i].instance.on('destroy', kff.bindFn(destroyQueue[i + 1].instance, 'destroyAll'));
			else destroyQueue[i].instance.on('destroy', kff.bindFn(this, 'startInit'));
		}

		if(destroyQueue[0]) destroyQueue[0].instance.destroyAll();
		else this.startInit();

		if(this.dispatcher)
		{
			this.dispatcher.trigger('route', {
				state: state
			});
		}
	},

	startInit: function()
	{
		var i, l, view, options = {},
			precedingViewNames = this.getPrecedingViews(this.newViewName),
			from = 0;

		if(this.rootElement) options = { element: this.rootElement };
		if(this.dispatcher) options.dispatcher = this.dispatcher;
		options.env = this.env;

		for(i = 0, l = precedingViewNames.length; i < l; i++)
		{
			if(i >= this.viewsQueue.length)
			{
				view = this.viewFactory.createView(precedingViewNames[i], options);
				view.setViewFactory(this.viewFactory);
				this.pushView({ name: precedingViewNames[i], instance: view });
			}
			else from = i + 1;
		}

		this.newViewName = null;
		if(this.getLastView()) this.getLastView().instance.on('render', kff.bindFn(this, 'cascadeState'));
		if(this.viewsQueue[from]) this.viewsQueue[from].instance.init();
		else this.cascadeState();
	},

	findSharedView: function(c1, c2)
	{
		var i, l,
			c1a = this.getPrecedingViews(c1),
			c2a = this.getPrecedingViews(c2),
			c = null;

		for(i = 0, l = c1a.length < c2a.length ? c1a.length : c2a.length; i < l; i++)
		{
			if(c1a[i] !== c2a[i]) break;
			c = c1a[i];
		}
		return c;
	},

	getPrecedingViews: function(viewName)
	{
		var c = viewName, a = [c];

		while(c)
		{
			c = this.viewFactory.getPrecedingView(c);
			if(c) a.unshift(c);
		}
		return a;
	},

	getViewFactory: function()
	{
		return this.viewFactory;
	},

	setViewFactory: function(viewFactory)
	{
		this.viewFactory = viewFactory;
	},

	getRouter: function()
	{
		return this.router;
	},

	setRouter: function(router)
	{
		this.router = router;
	},

	setDefaultView: function(defaultView)
	{
		this.defaultView = defaultView;
	},

	setStateHandler: function(stateHandler)
	{
		this.stateHandler = stateHandler;
	},

	setDispatcher: function(dispatcher)
	{
		this.dispatcher = dispatcher;
	}
});
<|endoftext|>


<commit_before>export default {
	"ca-es": "Catalan",
	"cs": "Czech",
	"en": "English",
	"es": "Spanish",
	"hu": "Hungarian",
	"fa": "Persian",
	"fr": "French",
	"kan": "Kannada",
	"pl": "Polish",
	"pt": "Portuguese",
	"ru": "Russian",
	"tr": "Turkish",
	"uk": "Ukrainian",
	"zh-cn": "Chinese Simplified",
	"zh-tw": "Traditional Chinese",
}
<commit_msg>Update lang-list.js<commit_after>export default {
	"ca-es": "Catalan",
	"cs": "Czech",
	"en": "English",
	"es": "Spanish",
	"hu": "Hungarian",
	"fa": "Persian",
	"fr": "French",
	"kan": "Kannada",
	"pl": "Polish",
	"pt": "Portuguese",
	"ru": "Russian",
	"tr": "Turkish",
	"uk": "Ukrainian",
	"ar": "العربية",
	"zh-cn": "Chinese Simplified",
	"zh-tw": "Traditional Chinese",
}
<|endoftext|>


<commit_before>/*!
 * chaindb.js - content data management for decentraland
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * Copyright (c) 2016-2017, Manuel Araoz (MIT License).
 * Copyright (c) 2016-2017, Esteban Ordano (MIT License).
 * Copyright (c) 2016-2017, The Decentraland Development Team (MIT License).
 * https://github.com/decentraland/decentraland-node
 */

'use strict';

var fs = require('fs');
var mkdirp = require('mkdirp');
var path = require('path');
var WebTorrent = require('webtorrent-hybrid');
var EventEmitter = require('events').EventEmitter;

var constants = require('../protocol/constants');
var util = require('../utils/util');
var co = require('../utils/co');

/**
 * WebTorrent-based content database
 * @exports ContentDB
 */
function ContentDB(chain) {
  if (!(this instanceof ContentDB))
    return new ContentDB(chain);

  EventEmitter.call(this);

  this.chain = chain;
  this.logger = chain.logger;
  this.network = chain.network;
  this.options = chain.options;
  this.webtorrent = new WebTorrent();
  var self = this;
  this.webtorrent.on('error', (err) => {
    self.logger.error('WebTorrent error:', err);
  });

  var dirPath = this._dirPath();
  mkdirp.sync(dirPath);

}
util.inherits(ContentDB, EventEmitter);

ContentDB.prototype._dirPath = function () {
  return this.options.prefix + 'tiles' + path.sep;
};

ContentDB.prototype._pathFor = function (x, y, hash) {
  var dirPath = this._dirPath();
  var namePath = x + '.' + y;
  if (hash) {
    namePath += '.' + hash;
  }
  namePath += '.tld';
  const fullPath = dirPath + namePath;
  return fullPath;
};

ContentDB.prototype._seed = co(function* _seed(x, y) {
  var self = this;
  const file = this._pathFor(x,y);
  const seedOpts = {
    name: `Decentraland tile (${x}, ${y})`,
    createdBy: 'decentraland',
  };
  yield new Promise(function(resolve, reject) {
    self.webtorrent.seed(fs.createReadStream(file), seedOpts,
      (torrent) => {
      self.logger.info('WebTorrent is seeding:', torrent.infoHash);
      self.logger.info('Total torrents:', self.webtorrent.torrents.length);
      resolve(torrent.infoHash);
    });
  });

});

ContentDB.prototype._copy = co(function* _copy(from, to) {
  yield fs.createReadStream(from)
    .pipe(fs.createWriteStream(to));
});

ContentDB.prototype.save = co(function* save(x, y, hash, stream, isCurrent) {
  const currentPath = this._pathFor(x, y);
  const historicPath = this._pathFor(x, y, hash);
  yield stream.pipe(fs.createWriteStream(historicPath));
  if (isCurrent) {
    this.logger.info(`Saving new version of ${x}, ${y} at ${historicPath}`);
    yield this._copy(historicPath, currentPath);
    yield this._seed(x , y);
  }
});

ContentDB.prototype.fetch = co(function* fetch(x, y, hash) {
  this.logger.info('ContentDB asked to fetch content', hash);

  // check if we already have that file
  if (fs.existsSync(this._pathFor(x, y, hash))) {
    return;
  }

  // if null content, save default tile without downloading torrent
  if (hash === constants.NULL_HASH) {
    const hola = fs.createReadStream('./data/default.tld');
    return this.save(x, y, hash, hola, true);
  }

  var self = this;

  this.webtorrent.add(hash, (torrent) => {
    // new torrent loaded for download
    self.logger.info(`WebTorrent new torrent loaded for ${x}, ${y}`,
      torrent.infoHash);

    torrent.on('ready', () => {
			self.logger.info('WebTorrent ready: ' + torrent.infoHash);
    });

    torrent.on('warning', (err) => {
      self.logger.warn('WebTorrent', err);
    });

		torrent.on('download', (bytes) => {
			self.logger.info('XXX just downloaded: ' + bytes);
			self.logger.info('XXX total downloaded: ' + torrent.downloaded);
			self.logger.info('XXX download speed: ' + torrent.downloadSpeed);
			self.logger.info('XXX progress: ' + torrent.progress);
		});

    torrent.on('done', () => {
      self.logger.info('WebTorrent finished downloading', torrent.infoHash);
      var content = torrent.files[0];
      self.emit('content', x, y, content, hash);
    });

  });
});

ContentDB.prototype.putFile = co(function* setTile(x, y, contentFile) {
  var self = this;
  // 1. Copy file to Decentraland folder
  if (!fs.existsSync(contentFile)) {
    throw new Error('File '+ contentFile +' not found');
  }

  const copyPath = this._pathFor(x,y);
  yield this._copy(contentFile, ccopyPath);
  self.logger.info('Tile file ('+x+','+y+') copy finished, starting to seed torrent');
  return self._seed(x, y);

});

module.exports = ContentDB;
<commit_msg>fix copy<commit_after>/*!
 * chaindb.js - content data management for decentraland
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * Copyright (c) 2016-2017, Manuel Araoz (MIT License).
 * Copyright (c) 2016-2017, Esteban Ordano (MIT License).
 * Copyright (c) 2016-2017, The Decentraland Development Team (MIT License).
 * https://github.com/decentraland/decentraland-node
 */

'use strict';

var fs = require('fs');
var mkdirp = require('mkdirp');
var path = require('path');
var WebTorrent = require('webtorrent-hybrid');
var EventEmitter = require('events').EventEmitter;
var pass = require('stream').PassThrough;

var constants = require('../protocol/constants');
var util = require('../utils/util');
var co = require('../utils/co');

/**
 * WebTorrent-based content database
 * @exports ContentDB
 */
function ContentDB(chain) {
  if (!(this instanceof ContentDB))
    return new ContentDB(chain);

  EventEmitter.call(this);

  this.chain = chain;
  this.logger = chain.logger;
  this.network = chain.network;
  this.options = chain.options;
  this.webtorrent = new WebTorrent();
  var self = this;
  this.webtorrent.on('error', (err) => {
    self.logger.error('WebTorrent error:', err);
  });

  var dirPath = this._dirPath();
  mkdirp.sync(dirPath);

}
util.inherits(ContentDB, EventEmitter);

ContentDB.prototype._dirPath = function () {
  return this.options.prefix + 'tiles' + path.sep;
};

ContentDB.prototype._pathFor = function (x, y, hash) {
  var dirPath = this._dirPath();
  var namePath = x + '.' + y;
  if (hash) {
    namePath += '.' + hash;
  }
  namePath += '.tld';
  const fullPath = dirPath + namePath;
  return fullPath;
};

ContentDB.prototype._seed = co(function* _seed(x, y) {
  var self = this;
  const file = this._pathFor(x,y);
  const seedOpts = {
    name: `Decentraland tile (${x}, ${y})`,
    createdBy: 'decentraland',
  };
  yield new Promise(function(resolve, reject) {
    self.webtorrent.seed(fs.createReadStream(file), seedOpts,
      (torrent) => {
      self.logger.info('WebTorrent is seeding:', torrent.infoHash);
      self.logger.info('Total torrents:', self.webtorrent.torrents.length);
      resolve(torrent.infoHash);
    });
  });

});

ContentDB.prototype._copy = co(function* _copy(from, to) {
  yield fs.createReadStream(from)
    .pipe(fs.createWriteStream(to));
});

ContentDB.prototype.save = co(function* save(x, y, hash, stream, isCurrent) {
  const currentPath = this._pathFor(x, y);
  const historicPath = this._pathFor(x, y, hash);
  var save = stream.pipe(fs.createWriteStream(historicPath));
  var self = this;
  save.on('finish', () => {
    if (isCurrent) {
      self.logger.info(`Saving new version of ${x}, ${y} at ${historicPath}`);
      this._copy(historicPath, currentPath);
      this._seed(x , y);
    }
  });
});

ContentDB.prototype.fetch = co(function* fetch(x, y, hash) {
  this.logger.info('ContentDB asked to fetch content', hash);

  // check if we already have that file
  if (fs.existsSync(this._pathFor(x, y, hash))) {
    return;
  }

  // if null content, save default tile without downloading torrent
  if (hash === constants.NULL_HASH) {
    const defaultContent = fs.createReadStream('./data/default.tld');
    return this.save(x, y, hash, defaultContent, true);
  }

  var self = this;

  this.webtorrent.add(hash, (torrent) => {
    // new torrent loaded for download
    self.logger.info(`WebTorrent new torrent loaded for ${x}, ${y}`,
      torrent.infoHash);

    torrent.on('ready', () => {
			self.logger.info('WebTorrent ready: ' + torrent.infoHash);
    });

    torrent.on('warning', (err) => {
      self.logger.warn('WebTorrent', err);
    });

		torrent.on('download', (bytes) => {
			self.logger.info('XXX just downloaded: ' + bytes);
			self.logger.info('XXX total downloaded: ' + torrent.downloaded);
			self.logger.info('XXX download speed: ' + torrent.downloadSpeed);
			self.logger.info('XXX progress: ' + torrent.progress);
		});

    torrent.on('done', () => {
      self.logger.info('WebTorrent finished downloading', torrent.infoHash);
      var content = torrent.files[0];
      self.emit('content', x, y, content, hash);
    });

  });
});

ContentDB.prototype.putFile = co(function* setTile(x, y, contentFile) {
  var self = this;
  // 1. Copy file to Decentraland folder
  if (!fs.existsSync(contentFile)) {
    throw new Error('File '+ contentFile +' not found');
  }

  const copyPath = this._pathFor(x,y);
  yield this._copy(contentFile, copyPath);
  self.logger.info('Tile file ('+x+','+y+') copy finished, starting to seed torrent');
  return self._seed(x, y);

});

module.exports = ContentDB;
<|endoftext|>