<commit_before>/*********************************************
 *  Copyright (c) 2014 AnsibleWorks, Inc.
 *
 *  Credentials.js
 *  Form definition for Credential model
 *
 */
 /**
 * @ngdoc function
 * @name forms.function:Credentials
 * @description This form is for adding/editing a Credential
*/
angular.module('CredentialFormDefinition', [])
    .value('CredentialForm', {

        addTitle: 'Create Credential', //Legend in add mode
        editTitle: '{{ name }}', //Legend in edit mode
        name: 'credential',
        well: true,
        forceListeners: true,

        actions: {
            stream: {
                ngClick: "showActivity()",
                awToolTip: "View Activity Stream",
                mode: 'edit'
            }
        },

        fields: {
            name: {
                label: 'Name',
                type: 'text',
                addRequired: true,
                editRequired: true,
                autocomplete: false
            },
            description: {
                label: 'Description',
                type: 'text',
                addRequired: false,
                editRequired: false
            },
            owner: {
                label: "Does this credential belong to a team or user?",
                type: 'radio_group',
                ngChange: "ownerChange()",
                options: [{
                    label: 'User',
                    value: 'user',
                    selected: true
                }, {
                    label: 'Team',
                    value: 'team'
                }],
                awPopOver: "
A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " +
                    "to the credential. Choosing a team shares the credential with all team members.
",
                dataTitle: 'Owner',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            user: {
                label: 'User that owns this credential',
                type: 'lookup',
                sourceModel: 'user',
                sourceField: 'username',
                ngClick: 'lookUpUser()',
                ngShow: "owner == 'user'",
                awRequiredWhen: {
                    variable: "user_required",
                    init: "false"
                }
            },
            team: {
                label: 'Team that owns this credential',
                type: 'lookup',
                sourceModel: 'team',
                sourceField: 'name',
                ngClick: 'lookUpTeam()',
                ngShow: "owner == 'team'",
                awRequiredWhen: {
                    variable: "team_required",
                    init: "false"
                }
            },
            kind: {
                label: 'Type',
                excludeModal: true,
                type: 'select',
                ngOptions: 'kind.label for kind in credential_kind_options',
                ngChange: 'kindChange()',
                addRequired: true,
                editRequired: true,
                awPopOver:'
\n' +
                        '
Machine
\n' +
                        '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
                        'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
                        'remote hosts.
' +
                        '
Source Control
\n' +
                        '
Used to check out and synchronize playbook repositories with a remote source control ' +
                        'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
                        'used on the Projects tab.
\n' +
                        '
Others (Cloud Providers)
\n' +
                        '
Access keys for authenticating to the specific ' +
                        'cloud provider, usually used for inventory sync ' +
                        'and deployment.
\n' +
                        '
\n',
                dataTitle: 'Type',
                dataPlacement: 'right',
                dataContainer: "body"
                // helpCollapse: [{
                //     hdr: 'Select a Credential Type',
                //     content: '
\n' +
                //         '
Machine
\n' +
                //         '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
                //         'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
                //         'remote hosts.
' +
                //         '
Source Control
\n' +
                //         '
Used to check out and synchronize playbook repositories with a remote source control ' +
                //         'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
                //         'used on the Projects tab.
\n' +
                //         '
Others (Cloud Providers)
\n' +
                //         '
Access keys for authenticating to the specific ' +
                //         'cloud provider, usually used for inventory sync ' +
                //         'and deployment.
\n' +
                //         '
\n'
                // }]
            },
            access_key: {
                label: 'Access Key',
                type: 'text',
                ngShow: "kind.value == 'aws'",
                awRequiredWhen: {
                    variable: "aws_required",
                    init: false
                },
                autocomplete: false,
                apiField: 'username'
            },
            secret_key: {
                label: 'Secret Key',
                type: 'password',
                ngShow: "kind.value == 'aws'",
                awRequiredWhen: {
                    variable: "aws_required",
                    init: false
                },
                autocomplete: false,
                ask: false,
                clear: false,
                apiField: 'passwowrd'
            },
            "host": {
                labelBind: 'hostLabel',
                type: 'text',
                ngShow: "kind.value == 'vmware'",
                autocomplete: false,
                awRequiredWhen: {
                    variable: 'host_required',
                    init: false
                }
            },
            "username": {
                labelBind: 'usernameLabel',
                type: 'text',
                ngShow: "kind.value && kind.value !== 'aws' && " +
                        "kind.value !== 'gce' && kind.value!=='azure'",
                awRequiredWhen: {
                    variable: 'username_required',
                    init: false
                },
                autocomplete: false
            },
            "email_address": {
                labelBind: 'usernameLabel',
                type: 'email',
                ngShow: "kind.value === 'gce'",
                awRequiredWhen: {
                    variable: 'email_required',
                    init: false
                },
                autocomplete: false,
                awPopOver: '
The email address assigned to the Google Compute Engine service account.
',
                dataTitle: 'Email',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "subscription_id": {
                labelBind: "usernameLabel",
                type: 'text',
                ngShow: "kind.value == 'azure'",
                awRequiredWhen: {
                    variable: 'subscription_required',
                    init: false
                },
                addRequired: false,
                editRequired: false,
                autocomplete: false,
                awPopOver: '
Subscription ID is an Azure construct, which is mapped to a username.
',
                dataTitle: 'Subscription ID',
                dataPlacement: 'right',
                dataContainer: "body"

            },
            "api_key": {
                label: 'API Key',
                type: 'password',
                ngShow: "kind.value == 'rax'",
                awRequiredWhen: {
                    variable: "rackspace_required",
                    init: false
                },
                autocomplete: false,
                ask: false,
                clear: false,
                // apiField: 'passwowrd'
            },
            "password": {
                label: 'Password',
                type: 'password',
                ngShow: "kind.value == 'scm' || kind.value == 'vmware'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('password_confirm')",
                ask: false,
                clear: false,
                associated: 'password_confirm',
                autocomplete: false,
                awRequiredWhen: {
                    variable: "password_required",
                    init: false
                }
            },
            "password_confirm": {
                label: 'Confirm Password',
                type: 'password',
                ngShow: "kind.value == 'scm' || kind.value == 'vmware'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'password',
                autocomplete: false,
                awRequiredWhen: {
                    variable: "password_required",
                    init: false
                }
            },
            "ssh_password": {
                label: 'SSH Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                ngChange: "clearPWConfirm('ssh_password_confirm')",
                addRequired: false,
                editRequired: false,
                ask: true,
                clear: true,
                associated: 'ssh_password_confirm',
                autocomplete: false
            },
            "ssh_password_confirm": {
                label: 'Confirm SSH Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'ssh_password',
                autocomplete: false
            },
            "ssh_key_data": {
                labelBind: 'sshKeyDataLabel',
                type: 'textarea',
                ngShow: "kind.value == 'ssh' || kind.value == 'scm' || " +
                        "kind.value == 'gce' || kind.value == 'azure'",
                awRequiredWhen: {
                    variable: 'key_required',
                    init: true
                },
                hintText: "{{ key_hint }}",
                addRequired: false,
                editRequired: false,
                awDropFile: true,
                'class': 'ssh-key-field',
                rows: 10,
                awPopOver: "SSH key description",
                awPopOverWatch:   "key_description",
                dataTitle: 'Help',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "ssh_key_unlock": {
                label: 'Key Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('ssh_key_unlock_confirm')",
                associated: 'ssh_key_unlock_confirm',
                ask: true,
                askShow: "kind.value == 'ssh'",  // Only allow ask for machine credentials
                clear: true
            },
            "ssh_key_unlock_confirm": {
                label: 'Confirm Key Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'  || kind.value == 'scm'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'ssh_key_unlock'
            },
            "login_method": {
                label: "Login Method", // FIXME: Confirm this label is ok?
                type: 'radio_group',
                ngChange: "loginMethodChange()",
                options: [{
                    label: 'None', // FIXME: Maybe 'Default' or 'SSH only' instead?
                    value: '',
                    selected: true
                }, {
                    label: 'Sudo',
                    value: 'sudo'
                }, {
                    label: 'Su',
                    value: 'su'
                }],
                awPopOver: "
A credential may optionally provide a sudo username and password or su username and password to use when running a playbook.
",
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "sudo_username": {
                label: 'Sudo Username',
                type: 'text',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },
            "sudo_password": {
                label: 'Sudo Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('sudo_password_confirm')",
                ask: true,
                clear: true,
                associated: 'sudo_password_confirm',
                autocomplete: false
            },
            "sudo_password_confirm": {
                label: 'Confirm Sudo Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'sudo_password',
                autocomplete: false
            },
            "su_username": {
                label: 'Su Username',
                type: 'text',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },
            "su_password": {
                label: 'Su Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('su_password_confirm')",
                ask: true,
                clear: true,
                associated: 'su_password_confirm',
                autocomplete: false
            },
            "su_password_confirm": {
                label: 'Confirm Su Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'su_password',
                autocomplete: false
            },
            "project": {
                label: "Project",
                type: 'text',
                ngShow: "kind.value == 'gce'",
                awRequiredWhen: {
                    variable: 'project_required',
                    init: false
                },
                awPopOver: "
The Project ID is the GCE assigned identification. It is constructed as two words followed by a three digit number.  Such as:

adjective-noun-000
",
                dataTitle: 'Project ID',
                dataPlacement: 'right',
                dataContainer: "body",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },

            "vault_password": {
                label: "Vault Password",
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('vault_password_confirm')",
                ask: true,
                clear: true,
                associated: 'vault_password_confirm',
                autocomplete: false
            },
            "vault_password_confirm": {
                label: "Confirm Vault Password",
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'vault_password',
                autocomplete: false
            }
        },

        buttons: {
            save: {
                label: 'Save',
                ngClick: 'formSave()', //$scope.function to call on click, optional
                ngDisabled: true //Disable when $pristine or $invalid, optional
            },
            reset: {
                ngClick: 'formReset()',
                ngDisabled: true //Disabled when $pristine
            }
        },

        related: {}

    });
<commit_msg>Only show login method for machine credentials.<commit_after>/*********************************************
 *  Copyright (c) 2014 AnsibleWorks, Inc.
 *
 *  Credentials.js
 *  Form definition for Credential model
 *
 */
 /**
 * @ngdoc function
 * @name forms.function:Credentials
 * @description This form is for adding/editing a Credential
*/
angular.module('CredentialFormDefinition', [])
    .value('CredentialForm', {

        addTitle: 'Create Credential', //Legend in add mode
        editTitle: '{{ name }}', //Legend in edit mode
        name: 'credential',
        well: true,
        forceListeners: true,

        actions: {
            stream: {
                ngClick: "showActivity()",
                awToolTip: "View Activity Stream",
                mode: 'edit'
            }
        },

        fields: {
            name: {
                label: 'Name',
                type: 'text',
                addRequired: true,
                editRequired: true,
                autocomplete: false
            },
            description: {
                label: 'Description',
                type: 'text',
                addRequired: false,
                editRequired: false
            },
            owner: {
                label: "Does this credential belong to a team or user?",
                type: 'radio_group',
                ngChange: "ownerChange()",
                options: [{
                    label: 'User',
                    value: 'user',
                    selected: true
                }, {
                    label: 'Team',
                    value: 'team'
                }],
                awPopOver: "
A credential must be associated with either a user or a team. Choosing a user allows only the selected user access " +
                    "to the credential. Choosing a team shares the credential with all team members.
",
                dataTitle: 'Owner',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            user: {
                label: 'User that owns this credential',
                type: 'lookup',
                sourceModel: 'user',
                sourceField: 'username',
                ngClick: 'lookUpUser()',
                ngShow: "owner == 'user'",
                awRequiredWhen: {
                    variable: "user_required",
                    init: "false"
                }
            },
            team: {
                label: 'Team that owns this credential',
                type: 'lookup',
                sourceModel: 'team',
                sourceField: 'name',
                ngClick: 'lookUpTeam()',
                ngShow: "owner == 'team'",
                awRequiredWhen: {
                    variable: "team_required",
                    init: "false"
                }
            },
            kind: {
                label: 'Type',
                excludeModal: true,
                type: 'select',
                ngOptions: 'kind.label for kind in credential_kind_options',
                ngChange: 'kindChange()',
                addRequired: true,
                editRequired: true,
                awPopOver:'
\n' +
                        '
Machine
\n' +
                        '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
                        'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
                        'remote hosts.
' +
                        '
Source Control
\n' +
                        '
Used to check out and synchronize playbook repositories with a remote source control ' +
                        'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
                        'used on the Projects tab.
\n' +
                        '
Others (Cloud Providers)
\n' +
                        '
Access keys for authenticating to the specific ' +
                        'cloud provider, usually used for inventory sync ' +
                        'and deployment.
\n' +
                        '
\n',
                dataTitle: 'Type',
                dataPlacement: 'right',
                dataContainer: "body"
                // helpCollapse: [{
                //     hdr: 'Select a Credential Type',
                //     content: '
\n' +
                //         '
Machine
\n' +
                //         '
Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
                //         'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
                //         'remote hosts.
' +
                //         '
Source Control
\n' +
                //         '
Used to check out and synchronize playbook repositories with a remote source control ' +
                //         'management system such as Git, Subversion (svn), or Mercurial (hg). These credentials are ' +
                //         'used on the Projects tab.
\n' +
                //         '
Others (Cloud Providers)
\n' +
                //         '
Access keys for authenticating to the specific ' +
                //         'cloud provider, usually used for inventory sync ' +
                //         'and deployment.
\n' +
                //         '
\n'
                // }]
            },
            access_key: {
                label: 'Access Key',
                type: 'text',
                ngShow: "kind.value == 'aws'",
                awRequiredWhen: {
                    variable: "aws_required",
                    init: false
                },
                autocomplete: false,
                apiField: 'username'
            },
            secret_key: {
                label: 'Secret Key',
                type: 'password',
                ngShow: "kind.value == 'aws'",
                awRequiredWhen: {
                    variable: "aws_required",
                    init: false
                },
                autocomplete: false,
                ask: false,
                clear: false,
                apiField: 'passwowrd'
            },
            "host": {
                labelBind: 'hostLabel',
                type: 'text',
                ngShow: "kind.value == 'vmware'",
                autocomplete: false,
                awRequiredWhen: {
                    variable: 'host_required',
                    init: false
                }
            },
            "username": {
                labelBind: 'usernameLabel',
                type: 'text',
                ngShow: "kind.value && kind.value !== 'aws' && " +
                        "kind.value !== 'gce' && kind.value!=='azure'",
                awRequiredWhen: {
                    variable: 'username_required',
                    init: false
                },
                autocomplete: false
            },
            "email_address": {
                labelBind: 'usernameLabel',
                type: 'email',
                ngShow: "kind.value === 'gce'",
                awRequiredWhen: {
                    variable: 'email_required',
                    init: false
                },
                autocomplete: false,
                awPopOver: '
The email address assigned to the Google Compute Engine service account.
',
                dataTitle: 'Email',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "subscription_id": {
                labelBind: "usernameLabel",
                type: 'text',
                ngShow: "kind.value == 'azure'",
                awRequiredWhen: {
                    variable: 'subscription_required',
                    init: false
                },
                addRequired: false,
                editRequired: false,
                autocomplete: false,
                awPopOver: '
Subscription ID is an Azure construct, which is mapped to a username.
',
                dataTitle: 'Subscription ID',
                dataPlacement: 'right',
                dataContainer: "body"

            },
            "api_key": {
                label: 'API Key',
                type: 'password',
                ngShow: "kind.value == 'rax'",
                awRequiredWhen: {
                    variable: "rackspace_required",
                    init: false
                },
                autocomplete: false,
                ask: false,
                clear: false,
                // apiField: 'passwowrd'
            },
            "password": {
                label: 'Password',
                type: 'password',
                ngShow: "kind.value == 'scm' || kind.value == 'vmware'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('password_confirm')",
                ask: false,
                clear: false,
                associated: 'password_confirm',
                autocomplete: false,
                awRequiredWhen: {
                    variable: "password_required",
                    init: false
                }
            },
            "password_confirm": {
                label: 'Confirm Password',
                type: 'password',
                ngShow: "kind.value == 'scm' || kind.value == 'vmware'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'password',
                autocomplete: false,
                awRequiredWhen: {
                    variable: "password_required",
                    init: false
                }
            },
            "ssh_password": {
                label: 'SSH Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                ngChange: "clearPWConfirm('ssh_password_confirm')",
                addRequired: false,
                editRequired: false,
                ask: true,
                clear: true,
                associated: 'ssh_password_confirm',
                autocomplete: false
            },
            "ssh_password_confirm": {
                label: 'Confirm SSH Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'ssh_password',
                autocomplete: false
            },
            "ssh_key_data": {
                labelBind: 'sshKeyDataLabel',
                type: 'textarea',
                ngShow: "kind.value == 'ssh' || kind.value == 'scm' || " +
                        "kind.value == 'gce' || kind.value == 'azure'",
                awRequiredWhen: {
                    variable: 'key_required',
                    init: true
                },
                hintText: "{{ key_hint }}",
                addRequired: false,
                editRequired: false,
                awDropFile: true,
                'class': 'ssh-key-field',
                rows: 10,
                awPopOver: "SSH key description",
                awPopOverWatch:   "key_description",
                dataTitle: 'Help',
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "ssh_key_unlock": {
                label: 'Key Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('ssh_key_unlock_confirm')",
                associated: 'ssh_key_unlock_confirm',
                ask: true,
                askShow: "kind.value == 'ssh'",  // Only allow ask for machine credentials
                clear: true
            },
            "ssh_key_unlock_confirm": {
                label: 'Confirm Key Password',
                type: 'password',
                ngShow: "kind.value == 'ssh'  || kind.value == 'scm'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'ssh_key_unlock'
            },
            "login_method": {
                label: "Login Method", // FIXME: Confirm this label is ok?
                type: 'radio_group',
                ngShow: "kind.value == 'ssh'",
                ngChange: "loginMethodChange()",
                options: [{
                    label: 'None', // FIXME: Maybe 'Default' or 'SSH only' instead?
                    value: '',
                    selected: true
                }, {
                    label: 'Sudo',
                    value: 'sudo'
                }, {
                    label: 'Su',
                    value: 'su'
                }],
                awPopOver: "
A credential may optionally provide a sudo username and password or su username and password to use when running a playbook.
",
                dataPlacement: 'right',
                dataContainer: "body"
            },
            "sudo_username": {
                label: 'Sudo Username',
                type: 'text',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },
            "sudo_password": {
                label: 'Sudo Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('sudo_password_confirm')",
                ask: true,
                clear: true,
                associated: 'sudo_password_confirm',
                autocomplete: false
            },
            "sudo_password_confirm": {
                label: 'Confirm Sudo Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'sudo'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'sudo_password',
                autocomplete: false
            },
            "su_username": {
                label: 'Su Username',
                type: 'text',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },
            "su_password": {
                label: 'Su Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('su_password_confirm')",
                ask: true,
                clear: true,
                associated: 'su_password_confirm',
                autocomplete: false
            },
            "su_password_confirm": {
                label: 'Confirm Su Password',
                type: 'password',
                ngShow: "kind.value == 'ssh' && login_method == 'su'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'su_password',
                autocomplete: false
            },
            "project": {
                label: "Project",
                type: 'text',
                ngShow: "kind.value == 'gce'",
                awRequiredWhen: {
                    variable: 'project_required',
                    init: false
                },
                awPopOver: "
The Project ID is the GCE assigned identification. It is constructed as two words followed by a three digit number.  Such as:

adjective-noun-000
",
                dataTitle: 'Project ID',
                dataPlacement: 'right',
                dataContainer: "body",
                addRequired: false,
                editRequired: false,
                autocomplete: false
            },

            "vault_password": {
                label: "Vault Password",
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                ngChange: "clearPWConfirm('vault_password_confirm')",
                ask: true,
                clear: true,
                associated: 'vault_password_confirm',
                autocomplete: false
            },
            "vault_password_confirm": {
                label: "Confirm Vault Password",
                type: 'password',
                ngShow: "kind.value == 'ssh'",
                addRequired: false,
                editRequired: false,
                awPassMatch: true,
                associated: 'vault_password',
                autocomplete: false
            }
        },

        buttons: {
            save: {
                label: 'Save',
                ngClick: 'formSave()', //$scope.function to call on click, optional
                ngDisabled: true //Disable when $pristine or $invalid, optional
            },
            reset: {
                ngClick: 'formReset()',
                ngDisabled: true //Disabled when $pristine
            }
        },

        related: {}

    });
<|endoftext|>


<commit_before>/*
 * Creep spawn managment
 *
 * Checks how many creeps are active
 * Spawns new creeps to fill out numbers as needed
 *
 * Priority:
 *  harvester, upgrader, builder, hauler, repairer
 *
 */

// managment modules
var manageRole      = require('manage.role');

var manageCreep = function() {
    this.role = manageRole;
};

manageCreep.prototype.cleanCreeps = function() {
    for(let name in Memory.creeps) {
        if(!Game.creeps[name]) {
            if (Memory.creeps[name].workId) {
                Game.Queue.work.removeCreep(name, Memory.creeps[name].workId);
            }
            if (C.DEBUG >= 2) { console.log('DEBUG - clearing non-existant creep memory name: ' + name + ' role: ' + Memory.creeps[name].role); }
            delete Memory.creeps[name];
        }
    }

    return true;
};

manageCreep.prototype.doManage = function() {
    this.cleanCreeps();

    for(let name in Game.creeps) {
        let creep = Game.creeps[name];
        if (!creep.memory.role || creep.spawning) { continue; }
        if (creep.isDespawnWarning()) {
            this.doDespawn(creep);
            continue;
        }

        this.role.doRole(creep);
    }

};

manageCreep.prototype.doDespawn = function(creep) {
    if (!creep) { return false; }

    if (!creep.memory.despawn || creep.memory.despawn == undefined) {
        creep.setDespawn();
    }

    if (creep.getOffExit()) { return true; }
    if (creep.memory.spawnRoom && creep.room.name != creep.memory.spawnRoom) {
        creep.moveToRoom(creep.memory.spawnRoom);
        return true;
    }

	if (!creep.room.getSpawn()) { return false; }

    if (creep.room.getDespawnContainer()) {
        creep.memory.goingTo = creep.room.getDespawnContainer();
    }

    if (!creep.memory.goingTo) {
        this.getDespawnContainer(creep);
    } else {
        this.doDespawnOnContainer(creep);
    }

    return true;
};

manageCreep.prototype.doDespawnOnContainer = function(creep) {
    if (!creep) { return false; }

    let target = Game.getObjectById(creep.memory.goingTo);
    if (!target) {
        creep.memory.goingTo = false;
        return false;
    }

    if (creep.pos.x == target.pos.x && creep.pos.y == target.pos.y) {
        if (creep.room.memory.deSpawnContainerId && creep.room.memory.spawnId) {
            if (C.DEBUG >= 1) { console.log("INFO - recycling " + creep.memory.role + " " + creep.name); }
            let roomSpawn = Game.getObjectById(creep.room.getSpawn());
            roomSpawn.recycleCreep(creep);
        } else {
            creep.suicide();
        }
    }

    creep.moveTo(target.pos.x, target.pos.y);

    return true;
};

manageCreep.prototype.getDespawnContainer = function(creep) {
    if (!creep) { return false; }

    let targets = creep.room.getContainers();
    if (targets.length == 0) {
        creep.suicide();

        return false;
    }
    targets = _.sortBy(targets, structure => creep.pos.getRangeTo(structure));

    return creep.setGoingTo(targets[0]);
};

module.exports = manageCreep;
<commit_msg>update<commit_after>/*
 * Creep spawn managment
 *
 * Checks how many creeps are active
 * Spawns new creeps to fill out numbers as needed
 *
 * Priority:
 *  harvester, upgrader, builder, hauler, repairer
 *
 */

// managment modules
var manageRole      = require('manage.role');

var manageCreep = function() {
    Memory.world = Memory.world || {};
    this.memory = Memory.world;

    this.role = manageRole;
};

manageCreep.prototype.cleanCreeps = function() {
    this.memory.creepCleanUp = this.memory.creepCleanUp || Game.time;

    if ((this.memory.creepCleanUp + C.MANAGE_WAIT_TICKS) > Game.time) {
        return true;
    }

    for(let name in Memory.creeps) {
        if(!Game.creeps[name]) {
            if (Memory.creeps[name].workId) {
                Game.Queue.work.removeCreep(name, Memory.creeps[name].workId);
            }
            if (C.DEBUG >= 2) { console.log('DEBUG - clearing non-existant creep memory name: ' + name + ' role: ' + Memory.creeps[name].role); }
            delete Memory.creeps[name];
        }
    }

    this.memory.creepCleanUp = Game.time;
    return true;
};

manageCreep.prototype.doManage = function() {
    this.cleanCreeps();

    for(let name in Game.creeps) {
        let creep = Game.creeps[name];
        if (!creep.memory.role || creep.spawning) { continue; }
        if (creep.isDespawnWarning()) {
            this.doDespawn(creep);
            continue;
        }

        this.role.doRole(creep);
    }

};

manageCreep.prototype.doDespawn = function(creep) {
    if (!creep) { return false; }

    if (!creep.memory.despawn || creep.memory.despawn == undefined) {
        creep.setDespawn();
    }

    if (creep.getOffExit()) { return true; }
    if (creep.memory.spawnRoom && creep.room.name != creep.memory.spawnRoom) {
        creep.moveToRoom(creep.memory.spawnRoom);
        return true;
    }

	if (!creep.room.getSpawn()) { return false; }

    if (creep.room.getDespawnContainer()) {
        creep.memory.goingTo = creep.room.getDespawnContainer();
    }

    if (!creep.memory.goingTo) {
        this.getDespawnContainer(creep);
    } else {
        this.doDespawnOnContainer(creep);
    }

    return true;
};

manageCreep.prototype.doDespawnOnContainer = function(creep) {
    if (!creep) { return false; }

    let target = Game.getObjectById(creep.memory.goingTo);
    if (!target) {
        creep.memory.goingTo = false;
        return false;
    }

    if (creep.pos.x == target.pos.x && creep.pos.y == target.pos.y) {
        if (creep.room.memory.deSpawnContainerId && creep.room.memory.spawnId) {
            if (C.DEBUG >= 1) { console.log("INFO - recycling " + creep.memory.role + " " + creep.name); }
            let roomSpawn = Game.getObjectById(creep.room.getSpawn());
            roomSpawn.recycleCreep(creep);
        } else {
            creep.suicide();
        }
    }

    creep.moveTo(target.pos.x, target.pos.y);

    return true;
};

manageCreep.prototype.getDespawnContainer = function(creep) {
    if (!creep) { return false; }

    let targets = creep.room.getContainers();
    if (targets.length == 0) {
        creep.suicide();

        return false;
    }
    targets = _.sortBy(targets, structure => creep.pos.getRangeTo(structure));

    return creep.setGoingTo(targets[0]);
};

module.exports = manageCreep;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Steam Auto-Wishlister
// @namespace    https://github.com/FiveElementNinja/Steam-Auto-Wishlister
// @version      0.2
// @description  Automatically adds a game to your Steam wishlist when viewing its store page if it has cards.
// @author       Five Element Ninja (@FivElementNinja)
// @match        *://store.steampowered.com/app/*
// @updateURL    https://github.com/FiveElementNinja/Steam-Auto-Wishlister/steam-auto-wishlister.user.js
// @downloadURL  https://github.com/FiveElementNinja/Steam-Auto-Wishlister/steam-auto-wishlister.user.js
// @grant        none
// @run-at       document-end
// ==/UserScript==

/**
* Using plain Javascript because it's overkill to load in a second instance of jQuery just for this little script.
*/

console.log('Loaded');

(function () {
    var category_icons = document.getElementsByClassName('category_icon');

    if (category_icons) {
        var has_cards = false;
        var is_free = false;
        var is_dlc = false;

        // check if this is a free app, in which case don't add it to wishlist
        var btn_addtocart = document.getElementsByClassName('btn_addtocart');
        if (btn_addtocart) {
            for (j=0; j < btn_addtocart.length; j++) {
                if (btn_addtocart[j].firstElementChild) {
                    console.log(btn_addtocart[j].firstElementChild);
                    if (btn_addtocart[j].firstElementChild.href.match('steam://run/')) {
                        is_free = true;
                        break;
                    }
                }
            }
        }

        // check if it has cards
        for (i=0; i < category_icons.length; i++) {
            if (category_icons[i].src.match('ico_cards')) {
                has_cards = true;
                break;
            }

            // check if this is a DLC app, in which case don't add it to wishlist
            if (category_icons[i].src.match('ico_dlc')) {
                is_dlc = true;
                break;
            }
        }

        // if it does have cards and isn't a F2P app or DLC, add it to the wishlist
        if (has_cards && !is_free && !is_dlc) {
            console.log('This app has cards. Adding to Wishlist.');

            var wishlist_area = document.getElementById('add_to_wishlist_area');
            if (wishlist_area && wishlist_area.firstElementChild && wishlist_area.firstElementChild.href && wishlist_area.firstElementChild.href != '' && wishlist_area.firstElementChild.href.indexOf('javascript:AddToWishlist(') === 0) {
                try {
                    // click the Add To Wishlist button
                    wishlist_area.firstElementChild.click();

                    // highlight the On Wishlist button to indicate that it has been auto-clicked
                    document.getElementsByClassName('queue_btn_active')[0].style.border ='1px solid #999999';
                } catch (err) {
                    console.log('Error adding to Wishlist. Try reloading the page.');
                }
            } else {
                console.log('This app is already on your Wishlist or there was an error.');
            }
        } else {
            console.log('This app does not have cards.');
        }
    } else {
        console.log('Script error.');
    }
}());<commit_msg>- Code cleanup. - Bug fixes for store pages with multiple Add to Card buttons, which should make the script not error out anymore. - Added script name to all console logs so you know where those messages are coming from. - Updated update and download URL fields to point to the correct URLs for automatic updating.<commit_after>// ==UserScript==
// @name         Steam Auto-Wishlister
// @namespace    https://github.com/FiveElementNinja/Steam-Auto-Wishlister
// @version      0.3
// @description  Automatically adds a game to your Steam wishlist when viewing its store page if it has cards.
// @author       Five Element Ninja (@FivElementNinja)
// @match        *://store.steampowered.com/app/*
// @updateURL    https://github.com/FiveElementNinja/Steam-Auto-Wishlister/raw/master/steam-auto-wishlister.user.js
// @downloadURL  https://github.com/FiveElementNinja/Steam-Auto-Wishlister/raw/master/steam-auto-wishlister.user.js
// @grant        none
// @run-at       document-end
// ==/UserScript==

/**
* Using plain Javascript because it's overkill to load in a second instance of jQuery just for this little script.
*/

console.log('Steam Auto-Wishlister -- Loaded');

(function () {
    var category_icons = document.getElementsByClassName('category_icon');

    if (category_icons) {
        var has_cards = false;
        var is_free = false;
        var is_dlc = false;

        // check if this is a free app, in which case don't add it to wishlist
        var btn_addtocart = document.getElementsByClassName('btn_addtocart');
        if (btn_addtocart) {
            for (var j=0; j < btn_addtocart.length; j++) {
                if (btn_addtocart[j].firstElementChild) {
                    //console.log(btn_addtocart[j].firstElementChild);
                    if (btn_addtocart[j].firstElementChild.href && btn_addtocart[j].firstElementChild.href.match('steam://run/')) {
                        is_free = true;
                        break;
                    }
                }
            }
        }

        // check if it has cards
        for (var i=0; i < category_icons.length; i++) {
            if (category_icons[i].src && category_icons[i].src.match('ico_cards')) {
                has_cards = true;
                break;
            }

            // check if this is a DLC app, in which case don't add it to wishlist
            if (category_icons[i].src && category_icons[i].src.match('ico_dlc')) {
                is_dlc = true;
                break;
            }
        }

        // if it does have cards and isn't a F2P app or DLC, add it to the wishlist
        if (has_cards && !is_free && !is_dlc) {
            console.log('Steam Auto-Wishlister -- This app has cards. Adding to Wishlist.');

            var wishlist_area = document.getElementById('add_to_wishlist_area');
            if (wishlist_area && wishlist_area.firstElementChild && wishlist_area.firstElementChild.href && wishlist_area.firstElementChild.href != '' && wishlist_area.firstElementChild.href.indexOf('javascript:AddToWishlist(') === 0) {
                try {
                    // click the Add To Wishlist button
                    wishlist_area.firstElementChild.click();

                    // highlight the On Wishlist button to indicate that it has been auto-clicked
                    document.getElementsByClassName('queue_btn_active')[0].style.border ='1px solid #999999';
                } catch (err) {
                    console.log('Steam Auto-Wishlister -- Error adding to Wishlist. Try reloading the page.');
                }
            } else {
                console.log('Steam Auto-Wishlister -- This app is already on your Wishlist or there was an error.');
            }
        } else {
            console.log('Steam Auto-Wishlister -- This app does not have cards.');
        }
    } else {
        console.log('Steam Auto-Wishlister -- Script error.');
    }
}());<|endoftext|>


<commit_before>window.addEventListener('message', function (event) {
	console.dir(event);
	// if (originWhitelist.includes(e.origin)) {
	if (e.origin) {
		const type = event && event.data && event.data.action;
		return handleMessage({ event, type, connector: new OnlyKeyConnector() });
	}
});

function handleMessage(params = {}) {
	const { event, type, connector } = params;
	if (!(event && type && connector)) {
		throw new ReferenceError(`params event, type, and connector are required.`);
	}

	switch(type) {
		case 'GET_CONNECTOR':
			e.source.postMessage({ data: event.data, connector, type }, e.origin);
			break;
		default:
			// unhandled type
			break;
	}
}

const originWhitelist = [
	'https://'
];

class OnlyKeyConnector {
	constructor(params) {
		this.Sign = auth_sign;
		this.Decrypt = auth_decrypt;
	}
}
<commit_msg>fix event var<commit_after>window.addEventListener('message', function (event) {
	console.dir(event);
	// if (originWhitelist.includes(event.origin)) {
	if (event.origin) {
		const type = event && event.data && event.data.action;
		return handleMessage({ event, type, connector: new OnlyKeyConnector() });
	}
});

function handleMessage(params = {}) {
	const { event, type, connector } = params;
	if (!(event && type && connector)) {
		throw new ReferenceError(`params event, type, and connector are required.`);
	}

	switch(type) {
		case 'GET_CONNECTOR':
			e.source.postMessage({ data: event.data, connector, type }, e.origin);
			break;
		default:
			// unhandled type
			break;
	}
}

const originWhitelist = [
	'https://'
];

class OnlyKeyConnector {
	constructor(params) {
		this.Sign = auth_sign;
		this.Decrypt = auth_decrypt;
	}
}
<|endoftext|>


<commit_before>/* global TestScript, DEFAULT_TRACING_RULES, Test */

// This test connects to the localhost domain, and runs two benchmarks in sequence.
// 1) it spawns 100 total animated model entities in front of the camera for 10 seconds.
// 2) it spawns 1000 total animated model entities in front of the camera for 10 seconds.
// Each model and animation is currently uploaded to the hifi public AWS server.
// But there are local copies in the tests/content/entity/model folder.

Script.include("./BenchmarkLib.js");

var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), {x: 0, y: 0.5, z: 0});

var entityProps = [
    {
        position: Vec3.sum(spawnPoint, Vec3.multiply(0.5, Quat.getRight(MyAvatar.orientation))),
        rotation: MyAvatar.orientation,
        dimensions: {x: 1.5601, y: 1.8097, z: 0.6777},
        modelURL: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/claire/Claire.fbx",
        name: "Claire",
        type: "Model",
        animation: {
            url: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/claire/Claire@t-pose_47.fbx",
            running: true,
            fps: 30,
            firstFrame: 0,
            lastFrame: 100000,
            loop: true
        }
    },
    {
        position: Vec3.sum(spawnPoint, Vec3.multiply(-0.5, Quat.getRight(MyAvatar.orientation))),
        rotation: MyAvatar.orientation,
        dimensions: {x: 1.9245, y: 2.0101, z: 0.7254},
        modelURL: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/kaya/Kaya.fbx",
        name: "Kaya",
        type: "Model",
        animation: {
            url: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/kaya/Kaya%40t-pose_46.fbx",
            running: true,
            fps: 30,
            firstFrame: 0,
            lastFrame: 100000,
            loop: true
        }
    }
];

var entities = [];
function spawnAnimatedModelEntities(count) {
    for (var i = 0; i < count / 2; i++) {
        var offset = Vec3.multiply(0.5, Quat.getFront(MyAvatar.orientation));
        entities = entities.concat(entityProps.map(function (prop) {
            prop.position = Vec3.sum(prop.position, offset);
            return Entities.addEntity(prop);
        }));
    }
}

function destroyAnimatedModelEntities() {
    entities.forEach(function (entity) {
        Entities.deleteEntity(entity);
    });
    entities = [];
    // reset positions
    entityProps[0].position = Vec3.sum(spawnPoint, Vec3.multiply(0.5, Quat.getRight(MyAvatar.orientation)));
    entityProps[1].position = Vec3.sum(spawnPoint, Vec3.multiply(-0.5, Quat.getRight(MyAvatar.orientation)));
}

var testScript = new TestScript();

testScript.addTest({
    name: "100 animated models",
    loader: TestScript.locationLoader("localhost",
							true,
							{ x: 0, y: 0, z: 0 },
							{ x: 0, y: 0, z: 0, w: 1 }),
    tracingRules: DEFAULT_TRACING_RULES,
    traceActions: function () {
        Test.startTraceEvent("spawningEntities");
        spawnAnimatedModelEntities(100);
    },
    traceCompletion: function () {
        destroyAnimatedModelEntities();
    },
    duration: 10
});

testScript.addTest({
    name: "1000 animated models",
    loader: TestScript.locationLoader("localhost",
							true,
							{ x: 0, y: 0, z: 0 },
							{ x: 0, y: 0, z: 0, w: 1 }),
    tracingRules: DEFAULT_TRACING_RULES,
    traceActions: function () {
        Test.startTraceEvent("spawningEntities");
        spawnAnimatedModelEntities(1000);
    },
    traceCompletion: function () {
        destroyAnimatedModelEntities();
    },
    duration: 10
});
testScript.runTests();

<commit_msg>whitespace fixes<commit_after>/* global TestScript, DEFAULT_TRACING_RULES, Test */

// This test connects to the localhost domain, and runs two benchmarks in sequence.
// 1) it spawns 100 total animated model entities in front of the camera for 10 seconds.
// 2) it spawns 1000 total animated model entities in front of the camera for 10 seconds.
// Each model and animation is currently uploaded to the hifi public AWS server.
// But there are local copies in the tests/content/entity/model folder.

Script.include("./BenchmarkLib.js");

var spawnPoint = Vec3.sum(Vec3.sum(MyAvatar.position, Vec3.multiply(1.0, Quat.getFront(MyAvatar.orientation))), {x: 0, y: 0.5, z: 0});

var entityProps = [
    {
        position: Vec3.sum(spawnPoint, Vec3.multiply(0.5, Quat.getRight(MyAvatar.orientation))),
        rotation: MyAvatar.orientation,
        dimensions: {x: 1.5601, y: 1.8097, z: 0.6777},
        modelURL: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/claire/Claire.fbx",
        name: "Claire",
        type: "Model",
        animation: {
            url: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/claire/Claire@t-pose_47.fbx",
            running: true,
            fps: 30,
            firstFrame: 0,
            lastFrame: 100000,
            loop: true
        }
    },
    {
        position: Vec3.sum(spawnPoint, Vec3.multiply(-0.5, Quat.getRight(MyAvatar.orientation))),
        rotation: MyAvatar.orientation,
        dimensions: {x: 1.9245, y: 2.0101, z: 0.7254},
        modelURL: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/kaya/Kaya.fbx",
        name: "Kaya",
        type: "Model",
        animation: {
            url: "https://hifi-public.s3.amazonaws.com/cozza13/apartment/kaya/Kaya%40t-pose_46.fbx",
            running: true,
            fps: 30,
            firstFrame: 0,
            lastFrame: 100000,
            loop: true
        }
    }
];

var entities = [];
function spawnAnimatedModelEntities(count) {
    for (var i = 0; i < count / 2; i++) {
        var offset = Vec3.multiply(0.5, Quat.getFront(MyAvatar.orientation));
        entities = entities.concat(entityProps.map(function (prop) {
            prop.position = Vec3.sum(prop.position, offset);
            return Entities.addEntity(prop);
        }));
    }
}

function destroyAnimatedModelEntities() {
    entities.forEach(function (entity) {
        Entities.deleteEntity(entity);
    });
    entities = [];
    // reset positions
    entityProps[0].position = Vec3.sum(spawnPoint, Vec3.multiply(0.5, Quat.getRight(MyAvatar.orientation)));
    entityProps[1].position = Vec3.sum(spawnPoint, Vec3.multiply(-0.5, Quat.getRight(MyAvatar.orientation)));
}

var testScript = new TestScript();

testScript.addTest({
    name: "100 animated models",
    loader: TestScript.locationLoader("localhost",
                            true,
                            { x: 0, y: 0, z: 0 },
                            { x: 0, y: 0, z: 0, w: 1 }),
    tracingRules: DEFAULT_TRACING_RULES,
    traceActions: function () {
        Test.startTraceEvent("spawningEntities");
        spawnAnimatedModelEntities(100);
    },
    traceCompletion: function () {
        destroyAnimatedModelEntities();
    },
    duration: 10
});

testScript.addTest({
    name: "1000 animated models",
    loader: TestScript.locationLoader("localhost",
                            true,
                            { x: 0, y: 0, z: 0 },
                            { x: 0, y: 0, z: 0, w: 1 }),
    tracingRules: DEFAULT_TRACING_RULES,
    traceActions: function () {
        Test.startTraceEvent("spawningEntities");
        spawnAnimatedModelEntities(1000);
    },
    traceCompletion: function () {
        destroyAnimatedModelEntities();
    },
    duration: 10
});
testScript.runTests();

<|endoftext|>


<commit_before>/**
 * @fileoverview Mock chrome extension api.
 */

goog.provide('ydn.testing.mockExtension');

/**
 * @define {string} extension origin
 */
ydn.testing.mockExtension.BASE_URL = '';

ydn.testing.mockExtension.storage_data_ = {};

ydn.testing.mockExtension.storage_ = {
  'get': function(obj, cb) {
    cb({});
  },
  'set': function(obj, cb) {
    cb({});
  }
};

ydn.testing.mockExtension.Storage = function() {
  this.data_ = {};
};

ydn.testing.mockExtension.Storage.prototype.get = function(name) {
  var obj = {};
  obj[name] = JSON.parse(JSON.stringify(this.data_[name] || null));
};

ydn.testing.mockExtension.Storage.prototype.set = function(name, obj) {
  if (obj) {
    this.data_[name] = JSON.parse(JSON.stringify(obj));
  } else {
    delete this.data_[name];
  }
};

if (!window.chrome) {
  window.chrome = {};
}

if (!chrome.extension) {
  chrome.extension = {
    'getURL': function(s) {
      if (!s) {
        return ydn.testing.mockExtension.BASE_URL;
      } else if (s.charAt(0) != '/' &&
          ydn.testing.mockExtension.BASE_URL.charAt(0) != '/') {
        s = '/' + s;
      } else if (s.charAt(0) == '/' &&
          ydn.testing.mockExtension.BASE_URL.charAt(0) == '/') {
        s = s.substring(1);
      }
      return ydn.testing.mockExtension.BASE_URL + s;
    }
  };
}

if (!chrome.storage) {
  chrome.storage = {
    'sync': new ydn.testing.mockExtension.Storage(),
    'local': new ydn.testing.mockExtension.Storage(),
    'onChanged': {
      'addListener': function() {}
    }
  };
}

if (!chrome.runtime) {
  chrome.runtime = {};
}
if (!chrome.runtime.getManifest) {
  chrome.runtime.getManifest = function () {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL('manifest.json'), false);
    var manifest = {};
    xhr.onload = function () {
      manifest = JSON.parse(xhr.responseText);
    };
    xhr.send();
    return manifest;
  }
}

if (!window['CRMinInbox']) {
  window['CRMinInbox'] = {"Version":{"release":"1.4.2"},"sugarcrm":{"Version":{"release":"0.18.3"}}};
}

<commit_msg>adding unit test for normally hide setting<commit_after>/**
 * @fileoverview Mock chrome extension api.
 */

goog.provide('ydn.testing.mockExtension');

/**
 * @define {string} extension origin
 */
ydn.testing.mockExtension.BASE_URL = '';

ydn.testing.mockExtension.Storage = function() {
  this.data_ = {};
};

ydn.testing.mockExtension.Storage.prototype.get = function(name, cb) {
  var obj = {};
  if (typeof name == 'string') {
    obj[name] = JSON.parse(JSON.stringify(this.data_[name] || null));
  } else {
    for (var i = 0; i < name.length; i++) {
      obj[name[i]] = JSON.parse(JSON.stringify(this.data_[name[i]] || null));
    }
  }
  setTimeout(function() {
    cb(obj);
  }, 1);

};

ydn.testing.mockExtension.Storage.prototype.set = function(name, obj, cb) {
  if (obj) {
    this.data_[name] = JSON.parse(JSON.stringify(obj));
  } else {
    delete this.data_[name];
  }
  if (cb) {
    setTimeout(function() {
      cb(obj);
    }, 1);
  }
};

ydn.testing.mockExtension.Storage.prototype.clear = function(cb) {
  this.data_ = {};
  if (cb) {
    setTimeout(function() {
      cb();
    }, 1);
  }
};

if (!window.chrome) {
  window.chrome = {};
}

if (!chrome.extension) {
  chrome.extension = {
    'getURL': function(s) {
      if (!s) {
        return ydn.testing.mockExtension.BASE_URL;
      } else if (s.charAt(0) != '/' &&
          ydn.testing.mockExtension.BASE_URL.charAt(0) != '/') {
        s = '/' + s;
      } else if (s.charAt(0) == '/' &&
          ydn.testing.mockExtension.BASE_URL.charAt(0) == '/') {
        s = s.substring(1);
      }
      return ydn.testing.mockExtension.BASE_URL + s;
    }
  };
}

if (!chrome.storage) {
  chrome.storage = {
    'sync': new ydn.testing.mockExtension.Storage(),
    'local': new ydn.testing.mockExtension.Storage(),
    'onChanged': {
      'addListener': function() {}
    }
  };
}

if (!chrome.runtime) {
  chrome.runtime = {};
}
if (!chrome.runtime.getManifest) {
  chrome.runtime.getManifest = function() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', chrome.extension.getURL('manifest.json'), false);
    var manifest = {};
    xhr.onload = function() {
      manifest = JSON.parse(xhr.responseText);
    };
    xhr.send();
    return manifest;
  };
}

if (!window['CRMinInbox']) {
  window['CRMinInbox'] = {'Version': {'release': '1.4.2'},'sugarcrm': {'Version': {'release': '0.18.3'}}};
}

<|endoftext|>


<commit_before>const EXPORT = ["WidgetEmbedder", "IconEmbedder"];

var getEntryURL = entryURL;
var getAddPageURL = addPageURL;

function WidgetEmbedder(doc) {
    this.site = SiteInfoSet.Article.get(doc);
    if (!this.site || this.site.data.disable) return;
    this.embedStyle(doc);
    this.doc = null;
    let pref = Prefs.bookmark;
    this._inNewTab = pref.get("link.openInNewTab");
    this._embedCounter = pref.get("embed.counter");
    this._embedComments = pref.get("embed.comments");
    this._embedAddButton = pref.get("embed.addButton");
    this._timerId = doc.defaultView.setTimeout(this.onTimer, WidgetEmbedder.INITIAL_DELAY, this);
    doc.addEventListener("GM_AutoPagerizeLoaded", this, false, true);
    doc.addEventListener("HB.PageInserted", this, false, true);
    doc.addEventListener("DOMNodeInserted", this, false);
}

const embedStrings =
    new Strings("chrome://hatenabookmark/locale/embed.properties");

extend(WidgetEmbedder, {
    INITIAL_DELAY:  50,
    MUTATION_DELAY: 350,

    IMAGE_API_PREFIX:   B_STATIC_HTTP + 'entry/image/',
    COMMENTS_IMAGE_URL: "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%0E%00%00%00%0D%08%06%00%00%00%99%DC_%7F%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%06bKGD%00%FF%00%FF%00%FF%A0%BD%A7%93%00%00%01%B9IDAT(%CF%8D%D0%B1n%13A%14%05%D0%F7f%DE%8C%1D3%0EA%C8%A0%15q%1C%2C%84%84%A0%A0p%89%F0%1F%20Y4%E1%23%A0%A1%A4I%C3%17%F8%07(%91%1B%0A%D7%14%08%C4'%A4%81%02%D92V%D6%12%B1%9DY%EF%EE%CC%DB%19%9A8e%E4%5B%DF%23%5D%5D%1C%8F%C7%0DcL%03%00%8C%D6%FA%20%C6h%10%B1%81%88%1A%00%20%C6%E8b%8C%1BD%B4%CE%B9%25%00Xk%ED%86%8C1%8D%18%E3%5D%A5T%22%A5lk%AD%EF%11%D1m!%C4-%00%80%10B%C6%CC%2B%E7%5CJDS%EF%FD%DC%18%03%04%00F)%95%7C%FE%9D%9C%CE.U%1Fn%C8%83%A6%FFv%F2h~%CA%CC%25i%AD%0F%A4%94%ED%D9%A5%EA%7Fy%7Bt%93%83%C1p%D2WJ%B5%85%10K%01%00%7BDt%07v%CCUwO%C4%18%A5%10B%ED%0A%85%10*%C6(%05%22V!%04%BF%2B%0C!xD%AC%04%00%E4%CC%7C%B1%2B%BC%EA%E6%E4%9C%5B%12%D1%B4%BD%CF%3F%06%C3%C9%8Bma%7B%D4%608%B9F%87M%FF%DD%7B%3Fe%E6%25%01%80%F5%DE%CF%DF%3C%3E%FFX%AB%D5%9E%D4%EB%F5%E3%0F_%CD%BB-zv%3F%FC%3Ay%9A%8D%8B%A2%F8%E3%9C%3B%2B%0A%3FGDK%D6%DA%8D1%06%CA%B2%C4%10BYU%D5%EC%B0Y%7B%3E%18N%5E%1E%ED%FB%9F%AF%1E%FE%FB%B4Z%F9%0BfNC%08%7F%11qi%AD%DDP%9E%E7e%B7%DB%E54M%B9%AA%AAl%BD%5E%9F%BF%3E%CE%DE%2B%A5%EA%88Xy%0F93gR%CA%2C%CB2%DB%E9t%8A%C5bQ%E1v%FFh4%92%ADVK%01%00%01%00%E5y%AE%B4%D6!%C6%C8EQ%F8%24I%7C%AF%D7%BB~%FF%3F%87%DA%E3%E2KUj6%00%00%00%00IEND%AEB%60%82",
    ADD_BUTTON_URL:     B_STATIC_HTTP + 'images/append.gif',

    STRING_SHOW_ENTRY_TITLE:   embedStrings.get('showEntryTitle'),
    STRING_SHOW_ENTRY_TEXT:    embedStrings.get('showEntryText'),
    STRING_SHOW_COMMENT_TITLE: embedStrings.get('showCommentTitle'),
    STRING_SHOW_COMMENT_TEXT:  embedStrings.get('showCommentText'),
    STRING_ADD_BOOKMARK_TITLE: embedStrings.get('addBookmarkTitle'),
    STRING_ADD_BOOKMARK_TEXT:  embedStrings.get('addBookmarkText'),

    STYLE: <![CDATA[
        .hBookmark-widget {
            text-decoration: none !important;
            margin: 0 0 0 2px;
            border: none !important;
            display: inline !important;
        }
        .hBookmark-widget > img {
            border: none;
            vertical-align: middle;
            -moz-force-broken-image-icon: 1;
        }
    ]]>.toString(),
});

extend(WidgetEmbedder.prototype, {
    embedStyle: function WE_embedStyle(doc) {
        let style = doc.createElementNS(XHTML_NS, "style");
        style.setAttribute("type", "text/css");
        style.textContent = WidgetEmbedder.STYLE + (this.site.data.style || "");
        let head = doc.getElementsByTagName("head").item(0);
        if (!head) return;
        head.appendChild(style);
    },

    embed: function WE_embed() {
        //p('WidgetEmbedder#embed on ' + this.site.url);
        this.doc = this.site.doc;
        this.site.queryAll("paragraph", this.doc)
                 .forEach(this.embedInParagraph, this);
        this.doc = null;
    },

    embedInParagraph: function WE_embedInParagraph(paragraph) {
        if (paragraph.hasAttributeNS(HB_NS, "annotation")) return;
        paragraph.setAttributeNS(HB_NS, "hb:annotation", "true");

        let link = this.site.query("link", paragraph) || paragraph;
        if (!link.href || !/^https?:\/\//.test(link.href)) return;
        let points = this.getInsertionPoints(paragraph, link);
        let xmls = this.createWidgetXMLs(link);
        let space = this.doc.createTextNode(" ");
        let fragment = this.doc.createDocumentFragment();
        fragment.appendChild(space.cloneNode(false));

        let counter = null, comments = null;
        if (points.addButton) {
            let f = fragment.cloneNode(true);
            f.appendChild(xml2dom(xmls[2], points.addButton));
            f.appendChild(space.cloneNode(false));
            points.addButton.insertNode(f);
        }
        if (points.comments) {
            if (!points.counter)
                delete xmls[1].@style;
            comments = xml2dom(xmls[1], points.comments);
            comments.setAttributeNS(HB_NS, "hb:url", link.href);
            let f = fragment.cloneNode(true);
            f.appendChild(comments);
            if (points.comments !== points.addButton)
                f.appendChild(space.cloneNode(false));
            points.comments.insertNode(f);
        }
        if (points.counter) {
            counter = xml2dom(xmls[0], points.counter);
            let img = counter.firstChild;
            img.addEventListener("load", onCounterEvent, false);
            img.addEventListener("error", onCounterEvent, false);
            img.addEventListener("abort", onCounterEvent, false);
            let f = fragment.cloneNode(true);
            f.appendChild(counter);
            if (points.counter !== points.comments)
                f.appendChild(space.cloneNode(false));
            points.counter.insertNode(f);
        }

        function onCounterEvent(event) {
            let target = event.target;
            if (event.type === "load" && target.naturalWidth !== 1) {
                counter.removeAttribute("style");
                if (comments)
                    comments.removeAttribute("style");
            }
            target.removeEventListener("load", onCounterEvent, false);
            target.removeEventListener("error", onCounterEvent, false);
            target.removeEventListener("abort", onCounterEvent, false);
        }
    },

    getInsertionPoints: function WE_getInsertionPoints(paragraph, link) {
        let existings = this.getExistingWidgets(paragraph, link);
        let counterPoint = null, commentsPoint = null, addButtonPoint = null;
        if (!existings.counter) {
            let anchor = existings.entry ||
                         existings.comments ||
                         existings.addButton;
            if (anchor) {
                counterPoint = this.doc.createRange();
                counterPoint.selectNode(anchor);
                counterPoint.collapse(anchor !== existings.entry);
            } else {
                counterPoint = this.getAnnotationPoint(paragraph, link);
            }
        }
        if (!existings.comments) {
            if (existings.counter) {
                commentsPoint = this.doc.createRange();
                commentsPoint.selectNode(existings.counter);
                commentsPoint.collapse(false);
            } else {
                commentsPoint = counterPoint;
            }
        }
        if (!existings.addButton) {
            if (existings.comments) {
                addButtonPoint = this.doc.createRange();
                addButtonPoint.selectNode(existings.comments);
                addButtonPoint.collapse(false);
            } else {
                addButtonPoint = commentsPoint;
            }
        }
        return {
            counter:   this._embedCounter   ? counterPoint   : null,
            comments:  this._embedComments  ? commentsPoint  : null,
            addButton: this._embedAddButton ? addButtonPoint : null,
        };
    },

    getExistingWidgets: function WE_getExistingWidgets(paragraph, link) {
        const url = iri2uri(link.href);
        const escapedURL = encodeURIComponent(url);
        const entryURL    = getEntryURL(link.href);
        const oldEntryURL = B_HTTP + 'entry/' + url.replace(/#/g, '%23');
        const imageAPIPrefix    = B_STATIC_HTTP + 'entry/image/';
        const oldImageAPIPrefix = B_HTTP + 'entry/image/';
        const addURL    = getAddPageURL(link.href);
        const oldAddURL = B_HTTP + 'append?' + escapedURL;
        const entryImagePrefix = 'http://d.hatena.ne.jp/images/b_entry';
        let widgets = {
            entry:     null,
            counter:   null,
            comments:  null,
            addButton: null,
        };
        Array.forEach(paragraph.getElementsByTagName('a'), function (a) {
            switch (a.href) {
            case entryURL:
            case oldEntryURL:
                let content = a.firstChild;
                if (!content) break;
                if (content.nodeType === Node.TEXT_NODE) {
                    if (content.nodeValue.indexOf(' user') !== -1) {
                        widgets.counter = a;
                        break;
                    }
                    if (!content.nextSibling) break;
                    content = content.nextSibling;
                }
                if (content instanceof Ci.nsIDOMHTMLImageElement) {
                    let src = content.src;
                    if (src.indexOf(imageAPIPrefix) === 0 ||
                        src.indexOf(oldImageAPIPrefix) === 0)
                        widgets.counter = a;
                    else if (src.indexOf(entryImagePrefix) === 0)
                        widgets.entry = a;
                }
                break;

            case addURL:
            case oldAddURL:
                widgets.addButton = a;
                break;
            }
        });
        widgets.comments = paragraph.getElementsByClassName("hatena-bcomment-view-icon").item(0);
        return widgets;
    },

    getAnnotationPoint: function WE_getAnnotationPoint(paragraph, link) {
        let annotation = this.site.query("annotation", paragraph) || link;
        if (annotation instanceof Ci.nsIDOMRange) return annotation;
        let point = this.doc.createRange();
        let position = this.site.data.annotationPosition ||
            ((annotation instanceof Ci.nsIDOMHTMLAnchorElement ||
              annotation instanceof Ci.nsIDOMHTMLBRElement ||
              annotation instanceof Ci.nsIDOMHTMLHRElement ||
              annotation instanceof Ci.nsIDOMHTMLImageElement ||
              annotation instanceof Ci.nsIDOMHTMLCanvasElement ||
              annotation instanceof Ci.nsIDOMHTMLObjectElement ||
              annotation instanceof Ci.nsIDOMHTMLInputElement ||
              annotation instanceof Ci.nsIDOMHTMLButtonElement ||
              annotation instanceof Ci.nsIDOMHTMLSelectElement ||
              annotation instanceof Ci.nsIDOMHTMLTextAreaElement)
             ? 'after' : 'last');
        if (position === 'before' || position === 'after')
            point.selectNode(annotation);
        else
            point.selectNodeContents(annotation);
        point.collapse(position === 'before' || position === 'start');
        return point;
    },

    createWidgetXMLs: function WE_createWidgetXMLs(link) {
        default xml namespace = XHTML_NS;
        // Since Vimperator overrides XML settings, we override them again.
        let xmlSettings = XML.settings();
        XML.setSettings();
        const WE = WidgetEmbedder;
        let url = link.href;
        let entryURL = getEntryURL(url);
        let xmls = <>

            ;
        </>;
        if (this._inNewTab)
            for each (let a in xmls)
                a.@target = '_blank';
        XML.setSettings(xmlSettings);
        return xmls;
    },

    handleEvent: function WE_handleEvent(event) {
        let doc = event.currentTarget;
        switch (event.type) {
        case "DOMNodeInserted":
            if (this._timerId) break;
            this._timerId = doc.defaultView.setTimeout(this.onTimer, WidgetEmbedder.MUTATION_DELAY, this);
            break;

        case "HB.PageInserted":
            doc.removeEventListener("DOMNodeInserted", this, false);
            /* FALL THROUGH */
        case "GM_AutoPagerizeNextPageLoaded":
            this.embed();
            break;

        case "GM_AutoPagerizeLoaded":
            doc.removeEventListener("GM_AutoPagerizeLoaded", this, false);
            doc.removeEventListener("DOMNodeInserted", this, false);
            doc.addEventListener("GM_AutoPagerizeNextPageLoaded", this, false, true);
            break;
        }
    },

    onTimer: function WE_onTimer(self) {
        self.embed();
        self._timerId = 0;
    },
});

function tryToEmbedWidgets(event) {
    let doc = event.target;
    if (Prefs.bookmark.get("embed.enabled") &&
        doc instanceof HTMLDocument &&
        HTTPCache.counter.isValid(doc.defaultView.location.href))
        new WidgetEmbedder(doc);
}

window.addEventListener("load", function WidgetEmbedder_BEGIN() {
    gBrowser.addEventListener("DOMContentLoaded", tryToEmbedWidgets, true);
}, false);

// "IconEmbedder" is deprecated.  Use "WidgetEmbedder".
var IconEmbedder = WidgetEmbedder;
<commit_msg>data URIの画像をBase64エンコード。<commit_after>const EXPORT = ["WidgetEmbedder", "IconEmbedder"];

var getEntryURL = entryURL;
var getAddPageURL = addPageURL;

function WidgetEmbedder(doc) {
    this.site = SiteInfoSet.Article.get(doc);
    if (!this.site || this.site.data.disable) return;
    this.embedStyle(doc);
    this.doc = null;
    let pref = Prefs.bookmark;
    this._inNewTab = pref.get("link.openInNewTab");
    this._embedCounter = pref.get("embed.counter");
    this._embedComments = pref.get("embed.comments");
    this._embedAddButton = pref.get("embed.addButton");
    this._timerId = doc.defaultView.setTimeout(this.onTimer, WidgetEmbedder.INITIAL_DELAY, this);
    doc.addEventListener("GM_AutoPagerizeLoaded", this, false, true);
    doc.addEventListener("HB.PageInserted", this, false, true);
    doc.addEventListener("DOMNodeInserted", this, false);
}

const embedStrings =
    new Strings("chrome://hatenabookmark/locale/embed.properties");

extend(WidgetEmbedder, {
    INITIAL_DELAY:  50,
    MUTATION_DELAY: 350,

    IMAGE_API_PREFIX:   B_STATIC_HTTP + 'entry/image/',
    COMMENTS_IMAGE_URL: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9%2FAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A%2FwD%2FoL2nkwAAAblJREFUKM%2BN0LFuE0EUBdD3Zt6MHTMOQcigFXEcLISEoKBwifAfIFk04SOgoaRJwxf4ByiRGwrXFAjEJ6SBAtkyVtYSsZ1Z7%2B7M2xmaOGXkW98jXV0cj8cNY0wDAIzW%2BiDGaBCxgYgaACDG6GKMG0S0zrklAFhr7YaMMY0Y412lVCKlbGut7xHRbSHELQCAEELGzCvnXEpEU%2B%2F93BgDBABGKZV8%2Fp2czi5VH27Ig6b%2FdvJofsrMJWmtD6SU7dml6n95e3STg8Fw0ldKtYUQSwEAe0R0B3bMVXdPxBilEELtCoUQKsYoBSJWIQS%2FKwwheESsBADkzHyxK7zq5uScWxLRtL3PPwbDyYttYXvUYDi5RodN%2F917P2XmJQGA9d7P3zw%2B%2F1ir1Z7U6%2FXjD1%2FNuy16dj%2F8OnmajYui%2BOOcOysKP0dES9bajTEGyrLEEEJZVdXssFl7PhhOXh7t%2B5%2BvHv77tFr5C2ZOQwh%2FEXFprd1Qnudlt9vlNE25qqpsvV6fvz7O3iul6ohYeQ85M2dSyizLMtvpdIrFYlHhdv9oNJKtVksBAAEA5XmutNYhxshFUfgkSXyv17t%2B%2Fz%2BH2uPiS1VqNgAAAABJRU5ErkJggg%3D%3D',
    ADD_BUTTON_URL:     'data:image/gif;base64,R0lGODlhEAAMAJECAP%2F%2F%2FxhBzv%2F%2F%2FwAAACH5BAEAAAIALAAAAAAQAAwAAAIjVI6ZBu3%2FTlNOAovD1JfnDXZJ%2BIGl1UFlelLpC8WXodSHUAAAOw%3D%3D',
    //ADD_BUTTON_URL:     B_STATIC_HTTP + 'images/append.gif',

    STRING_SHOW_ENTRY_TITLE:   embedStrings.get('showEntryTitle'),
    STRING_SHOW_ENTRY_TEXT:    embedStrings.get('showEntryText'),
    STRING_SHOW_COMMENT_TITLE: embedStrings.get('showCommentTitle'),
    STRING_SHOW_COMMENT_TEXT:  embedStrings.get('showCommentText'),
    STRING_ADD_BOOKMARK_TITLE: embedStrings.get('addBookmarkTitle'),
    STRING_ADD_BOOKMARK_TEXT:  embedStrings.get('addBookmarkText'),

    STYLE: <![CDATA[
        .hBookmark-widget {
            text-decoration: none !important;
            margin: 0 0 0 2px;
            border: none !important;
            display: inline !important;
        }
        .hBookmark-widget > img {
            border: none;
            vertical-align: middle;
            -moz-force-broken-image-icon: 1;
        }
    ]]>.toString(),
});

extend(WidgetEmbedder.prototype, {
    embedStyle: function WE_embedStyle(doc) {
        let style = doc.createElementNS(XHTML_NS, "style");
        style.setAttribute("type", "text/css");
        style.textContent = WidgetEmbedder.STYLE + (this.site.data.style || "");
        let head = doc.getElementsByTagName("head").item(0);
        if (!head) return;
        head.appendChild(style);
    },

    embed: function WE_embed() {
        //p('WidgetEmbedder#embed on ' + this.site.url);
        this.doc = this.site.doc;
        this.site.queryAll("paragraph", this.doc)
                 .forEach(this.embedInParagraph, this);
        this.doc = null;
    },

    embedInParagraph: function WE_embedInParagraph(paragraph) {
        if (paragraph.hasAttributeNS(HB_NS, "annotation")) return;
        paragraph.setAttributeNS(HB_NS, "hb:annotation", "true");

        let link = this.site.query("link", paragraph) || paragraph;
        if (!link.href || !/^https?:\/\//.test(link.href)) return;
        let points = this.getInsertionPoints(paragraph, link);
        let xmls = this.createWidgetXMLs(link);
        let space = this.doc.createTextNode(" ");
        let fragment = this.doc.createDocumentFragment();
        fragment.appendChild(space.cloneNode(false));

        let counter = null, comments = null;
        if (points.addButton) {
            let f = fragment.cloneNode(true);
            f.appendChild(xml2dom(xmls[2], points.addButton));
            f.appendChild(space.cloneNode(false));
            points.addButton.insertNode(f);
        }
        if (points.comments) {
            if (!points.counter)
                delete xmls[1].@style;
            comments = xml2dom(xmls[1], points.comments);
            comments.setAttributeNS(HB_NS, "hb:url", link.href);
            let f = fragment.cloneNode(true);
            f.appendChild(comments);
            if (points.comments !== points.addButton)
                f.appendChild(space.cloneNode(false));
            points.comments.insertNode(f);
        }
        if (points.counter) {
            counter = xml2dom(xmls[0], points.counter);
            let img = counter.firstChild;
            img.addEventListener("load", onCounterEvent, false);
            img.addEventListener("error", onCounterEvent, false);
            img.addEventListener("abort", onCounterEvent, false);
            let f = fragment.cloneNode(true);
            f.appendChild(counter);
            if (points.counter !== points.comments)
                f.appendChild(space.cloneNode(false));
            points.counter.insertNode(f);
        }

        function onCounterEvent(event) {
            let target = event.target;
            if (event.type === "load" && target.naturalWidth !== 1) {
                counter.removeAttribute("style");
                if (comments)
                    comments.removeAttribute("style");
            }
            target.removeEventListener("load", onCounterEvent, false);
            target.removeEventListener("error", onCounterEvent, false);
            target.removeEventListener("abort", onCounterEvent, false);
        }
    },

    getInsertionPoints: function WE_getInsertionPoints(paragraph, link) {
        let existings = this.getExistingWidgets(paragraph, link);
        let counterPoint = null, commentsPoint = null, addButtonPoint = null;
        if (!existings.counter) {
            let anchor = existings.entry ||
                         existings.comments ||
                         existings.addButton;
            if (anchor) {
                counterPoint = this.doc.createRange();
                counterPoint.selectNode(anchor);
                counterPoint.collapse(anchor !== existings.entry);
            } else {
                counterPoint = this.getAnnotationPoint(paragraph, link);
            }
        }
        if (!existings.comments) {
            if (existings.counter) {
                commentsPoint = this.doc.createRange();
                commentsPoint.selectNode(existings.counter);
                commentsPoint.collapse(false);
            } else {
                commentsPoint = counterPoint;
            }
        }
        if (!existings.addButton) {
            if (existings.comments) {
                addButtonPoint = this.doc.createRange();
                addButtonPoint.selectNode(existings.comments);
                addButtonPoint.collapse(false);
            } else {
                addButtonPoint = commentsPoint;
            }
        }
        return {
            counter:   this._embedCounter   ? counterPoint   : null,
            comments:  this._embedComments  ? commentsPoint  : null,
            addButton: this._embedAddButton ? addButtonPoint : null,
        };
    },

    getExistingWidgets: function WE_getExistingWidgets(paragraph, link) {
        const url = iri2uri(link.href);
        const escapedURL = encodeURIComponent(url);
        const entryURL    = getEntryURL(link.href);
        const oldEntryURL = B_HTTP + 'entry/' + url.replace(/#/g, '%23');
        const imageAPIPrefix    = B_STATIC_HTTP + 'entry/image/';
        const oldImageAPIPrefix = B_HTTP + 'entry/image/';
        const addURL    = getAddPageURL(link.href);
        const oldAddURL = B_HTTP + 'append?' + escapedURL;
        const entryImagePrefix = 'http://d.hatena.ne.jp/images/b_entry';
        let widgets = {
            entry:     null,
            counter:   null,
            comments:  null,
            addButton: null,
        };
        Array.forEach(paragraph.getElementsByTagName('a'), function (a) {
            switch (a.href) {
            case entryURL:
            case oldEntryURL:
                let content = a.firstChild;
                if (!content) break;
                if (content.nodeType === Node.TEXT_NODE) {
                    if (content.nodeValue.indexOf(' user') !== -1) {
                        widgets.counter = a;
                        break;
                    }
                    if (!content.nextSibling) break;
                    content = content.nextSibling;
                }
                if (content instanceof Ci.nsIDOMHTMLImageElement) {
                    let src = content.src;
                    if (src.indexOf(imageAPIPrefix) === 0 ||
                        src.indexOf(oldImageAPIPrefix) === 0)
                        widgets.counter = a;
                    else if (src.indexOf(entryImagePrefix) === 0)
                        widgets.entry = a;
                }
                break;

            case addURL:
            case oldAddURL:
                widgets.addButton = a;
                break;
            }
        });
        widgets.comments = paragraph.getElementsByClassName("hatena-bcomment-view-icon").item(0);
        return widgets;
    },

    getAnnotationPoint: function WE_getAnnotationPoint(paragraph, link) {
        let annotation = this.site.query("annotation", paragraph) || link;
        if (annotation instanceof Ci.nsIDOMRange) return annotation;
        let point = this.doc.createRange();
        let position = this.site.data.annotationPosition ||
            ((annotation instanceof Ci.nsIDOMHTMLAnchorElement ||
              annotation instanceof Ci.nsIDOMHTMLBRElement ||
              annotation instanceof Ci.nsIDOMHTMLHRElement ||
              annotation instanceof Ci.nsIDOMHTMLImageElement ||
              annotation instanceof Ci.nsIDOMHTMLCanvasElement ||
              annotation instanceof Ci.nsIDOMHTMLObjectElement ||
              annotation instanceof Ci.nsIDOMHTMLInputElement ||
              annotation instanceof Ci.nsIDOMHTMLButtonElement ||
              annotation instanceof Ci.nsIDOMHTMLSelectElement ||
              annotation instanceof Ci.nsIDOMHTMLTextAreaElement)
             ? 'after' : 'last');
        if (position === 'before' || position === 'after')
            point.selectNode(annotation);
        else
            point.selectNodeContents(annotation);
        point.collapse(position === 'before' || position === 'start');
        return point;
    },

    createWidgetXMLs: function WE_createWidgetXMLs(link) {
        default xml namespace = XHTML_NS;
        // Since Vimperator overrides XML settings, we override them again.
        let xmlSettings = XML.settings();
        XML.setSettings();
        const WE = WidgetEmbedder;
        let url = link.href;
        let entryURL = getEntryURL(url);
        let xmls = <>

            ;
        </>;
        if (this._inNewTab)
            for each (let a in xmls)
                a.@target = '_blank';
        XML.setSettings(xmlSettings);
        return xmls;
    },

    handleEvent: function WE_handleEvent(event) {
        let doc = event.currentTarget;
        switch (event.type) {
        case "DOMNodeInserted":
            if (this._timerId) break;
            this._timerId = doc.defaultView.setTimeout(this.onTimer, WidgetEmbedder.MUTATION_DELAY, this);
            break;

        case "HB.PageInserted":
            doc.removeEventListener("DOMNodeInserted", this, false);
            /* FALL THROUGH */
        case "GM_AutoPagerizeNextPageLoaded":
            this.embed();
            break;

        case "GM_AutoPagerizeLoaded":
            doc.removeEventListener("GM_AutoPagerizeLoaded", this, false);
            doc.removeEventListener("DOMNodeInserted", this, false);
            doc.addEventListener("GM_AutoPagerizeNextPageLoaded", this, false, true);
            break;
        }
    },

    onTimer: function WE_onTimer(self) {
        self.embed();
        self._timerId = 0;
    },
});

function tryToEmbedWidgets(event) {
    let doc = event.target;
    if (Prefs.bookmark.get("embed.enabled") &&
        doc instanceof HTMLDocument &&
        HTTPCache.counter.isValid(doc.defaultView.location.href))
        new WidgetEmbedder(doc);
}

window.addEventListener("load", function WidgetEmbedder_BEGIN() {
    gBrowser.addEventListener("DOMContentLoaded", tryToEmbedWidgets, true);
}, false);

// "IconEmbedder" is deprecated.  Use "WidgetEmbedder".
var IconEmbedder = WidgetEmbedder;
<|endoftext|>