问:
Firefox WebExtension: How Do I Run Code Prior to Disable/Uninstall?
I have recently converted a GreaseMonkey script of mine into a WebExtension, just to get a first impression of the process. Now I have a reached a point where it would be nice to do some clean-up or simply undo all my changes when said extension is disabled/uninstalled.

From what I've seen on Mozilla's pages, runtime.onSuspend should do the trick. Unfortunately, it looks like that's not yet implemented (I'm on the regular Firefox release channel).

In other words, what I want to do is run code as a result of the user removing/disabling my extension so that I can clean-up listeners and such and generally restore the tabs to their status quo, i. e., undo all the changes the extension made.

答:
The other answer is incorrect. The first part (about the onSuspend event) is factually incorrect. The part about setUninstallURL is relevant, but does not answer the question since it does not allow you to restore tabs to their original state (as you asked in the question).

In this answer I will first clear the misconception about runtime.onSuspend, and then explain how you can run code for a content script when an extension is disabled.

About runtime.onSuspend

The chrome.runtime.onSuspend and chrome.runtime.onSuspendCanceled events have nothing to do with a disabled/uninstalled extension. The events are defined for event pages, which are basically background pages that are suspended (unloaded) after a period of inactivity. When the event page is about to unload due to suspension, runtime.onSuspend is called. If an extension API is called during this event (e.g. sending an extension message), the suspension will be canceled and trigger the onSuspendCanceled event.

When an extension is unloading because of a browser shutdown or an uninstallation, the lifetime of the extension cannot be extended. Thus you cannot rely on these events to run asynchronous tasks (such as cleaning up tabs from the background page).

Furthermore, these events are not available in content scripts (only extension pages such as background pages), so these cannot be used to synchronously clean up content script logic.

From the above it should be obvious that runtime.onSuspend is not remotely relevant for the goal of clean-up upon disable. Not in Chrome, let alone Firefox (Firefox does not support event pages, these events would be meaningless).

Running code in tabs/content scripts upon extension disable/uninstall

A common pattern in Chrome extensions is to use the port.onDisconnect event to detect that the background page has unloaded, and use that to infer that the extension might have unloaded (combined with option 1 of this method for a higher accuracy). Chrome's content scripts are kept around after an extension is disabled, so this can be used to run asynchronous clean-up code.

This is not possible in Firefox, because the execution context of a content script is destroyed when a Firefox extension is disabled, before the port.onDisconnect event has a chance to fire (at least, until bugzil.la/1223425 is fixed).

Despite these constraints, it is still possible to run clean up logic for a content script when an add-on is disabled. This method is based on the fact that in Firefox, style sheets inserted with tabs.insertCSS are removed when an add-on is disabled.

I will discuss two ways to exploit this characteristic. The first method allows execution of arbitrary code. The second method does not provide execution of arbitrary code, but it is simpler and sufficient if you only want to hide some extension-inserted DOM elements.

Method 1: Run code in page when extension is disabled

One of the ways to observe style changes is by declaring CSS transitions and using transition events to detect CSS property changes.
For this to be helpful, you need to construct a style sheet in such a way that it only affects your HTML elements. So you need to generate a unique selector (class name, ID, ...) and use that for your HTML element(s) and style sheet.

This is code that you have to put in your background script:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message !== 'getStyleCanary') return;

    // Generate a random class name, insert a style sheet and send
    // the class back to the caller if successful.
    var CANARY_CLASS = '_' + crypto.getRandomValues(new Uint32Array(2)).join('');
    var code = '.' + CANARY_CLASS + ' { opacity: 0 !important; }';
    chrome.tabs.insertCSS(sender.tab.id, {
        code,
        frameId: sender.frameId,
        runAt: 'document_start',
    }, function() {
        if (chrome.runtime.lastError) {
            // Failed to inject. Frame unloaded?
            sendResponse();
        } else {
            sendResponse(CANARY_CLASS);
        }
    });
    return true; // We will asynchronously call sendResponse.
});
```

<p>In the content script:</p>

<pre class="lang-js prettyprint-override"><code>chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // NOTE: Consider retry sending the message in case
        // the background page was not ready yet.
        return;
    }

    var s = document.createElement('script');
    s.src = chrome.runtime.getURL('canaryscript.js');
    s.onload = s.remove;
    s.dataset.canaryClass = CANARY_CLASS;

    // This function will become available to the page and be used
    // by canaryscript.js. NOTE: exportFunction is Firefox-only.
    exportFunction(function() {}, s, {defineAs: 'checkCanary'});

    (document.body || document.documentElement).appendChild(s);
});
</code>```

I use a script tag above, because it is the only way to run a script in the page without being blocked by the page's content security policy. Make sure that you add canaryscript.js to web_accessible_resources in manifest.json, or else the script won't load.

If running the cleanup code is not critical (e.g. because you also use method 2 which I explain later), then you should preferably use inline scripts instead of external scripts (i.e. use s.textContent = '<content of canaryscript.js>' instead of s.src = ...). This is because using .src with extension resources introduces a fingerprinting vulnerability to Firefox (bug 1372288).

This is the content of canaryscript.js:

(function() {
    // Thes two properties are set in the content script.
    var checkCanary = document.currentScript.checkCanary;
    var CANARY_CLASS = document.currentScript.dataset.canaryClass;

    var canary = document.createElement('span');
    canary.className = CANARY_CLASS;
    // The inserted style sheet has opacity:0. Upon removal a transition occurs.
    canary.style.opacity = '1';
    canary.style.transitionProperty = 'opacity';
    // Wait a short while to make sure that the content script destruction
    // finishes before the style sheet is removed.
    canary.style.transitionDelay = '100ms';
    canary.style.transitionDuration = '1ms';
    canary.addEventListener('transitionstart', function() {
       // To avoid inadvertently running clean-up logic when the event
       // is triggered by other means, check whether the content script
       // was really destroyed.
       try {
            // checkCanary will throw if the content script was destroyed.
            checkCanary();
            // If we got here, the content script is still valid.
            return;
        } catch (e) {
        }
        canary.remove();

        // TODO: Put the rest of your clean up code here.
    });
    (document.body || document.documentElement).appendChild(canary);
})();
```

<p>Note: CSS transition events are only fired if the tab is active. If the tab is inactive, the transition event will not fire until the tab is shown.</p>

<p>Note: <code>exportFunction</code> is a Firefox-only extension method to define a function in a different execution context (in the above example, the function was defined in the page's context, available to scripts running in that page).</p>

<p>All other APIs are available in other browsers too (Chrome/Opera/Edge), but the code cannot be used to detect disabled extensions, because style sheets from <code>tabs.insertCSS</code> are not removed upin uninstall (I only tested with Chrome; it might work in Edge).</p>

<h3>Method 2: Visual restoration upon uninstallation</h3>

<p>Method 1 allows you to run arbitrary code, such as removing all elements that you inserted in the page. As an alternative to removing the elements from the DOM, you can also choose to hide the elements through CSS.<br>
Below I show how you can modify method 1 to hide the elements without running other code (such as <code>canaryscript.js</code>).</p>

<p>When your content script creates an element for insertion in the DOM, you hide it with an inline style:</p>

<pre class="lang-js prettyprint-override"><code>var someUI = document.createElement('div');
someUI.style.display = 'none'; // <-- Hidden
// CANARY_CLASS is the random class (prefix) from the background page.
someUI.classList.add(CANARY_CLASS + 'block');
// ... other custom logic, and add to document.
</code>```

In the style sheet that you add with tabs.insertCSS, you then define the desired display value, with the !important flag so that the inline style is overridden:

// Put this snippet after "var code = '.' + CANARY_CLASS, above.
code += '.' + CANARY_CLASS + 'block {display: block !important;}';
```

<p>The above example is intentionally generic. If you have multiple UI elements with different CSS <code>display</code> values (e.g. <code>block</code>, <code>inline</code>, ...), then you can add multiple of these lines to re-use the framework that I provided.</p>

<p>To show the simplicity of method 2 over method 1: you can use the same background script (with the above modification), and use the following in the content script:</p>

<pre class="lang-js prettyprint-override"><code>// Example: Some UI in the content script that you want to clean up.
var someUI = document.createElement('div');
someUI.textContent = 'Example: This is a test';
document.body.appendChild(someUI);

// Clean-up is optional and a best-effort attempt.
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // Do not add clean-up classes.
        return;
    }
    someUI.classList.add(CANARY_CLASS + 'block');
    someUI.style.display = 'none';
});
</code>```

If your extension has more than one element, consider caching the value of CANARY_CLASS in a local variable so that you only insert one new style sheet per execution context.

Body:
The other answer is incorrect. The first part (about the onSuspend event) is factually incorrect. The part about setUninstallURL is relevant, but does not answer the question since it does not allow you to restore tabs to their original state (as you asked in the question).

In this answer I will first clear the misconception about runtime.onSuspend, and then explain how you can run code for a content script when an extension is disabled.

About runtime.onSuspend

The chrome.runtime.onSuspend and chrome.runtime.onSuspendCanceled events have nothing to do with a disabled/uninstalled extension. The events are defined for event pages, which are basically background pages that are suspended (unloaded) after a period of inactivity. When the event page is about to unload due to suspension, runtime.onSuspend is called. If an extension API is called during this event (e.g. sending an extension message), the suspension will be canceled and trigger the onSuspendCanceled event.

When an extension is unloading because of a browser shutdown or an uninstallation, the lifetime of the extension cannot be extended. Thus you cannot rely on these events to run asynchronous tasks (such as cleaning up tabs from the background page).

Furthermore, these events are not available in content scripts (only extension pages such as background pages), so these cannot be used to synchronously clean up content script logic.

From the above it should be obvious that runtime.onSuspend is not remotely relevant for the goal of clean-up upon disable. Not in Chrome, let alone Firefox (Firefox does not support event pages, these events would be meaningless).

Running code in tabs/content scripts upon extension disable/uninstall

A common pattern in Chrome extensions is to use the port.onDisconnect event to detect that the background page has unloaded, and use that to infer that the extension might have unloaded (combined with option 1 of this method for a higher accuracy). Chrome's content scripts are kept around after an extension is disabled, so this can be used to run asynchronous clean-up code.

This is not possible in Firefox, because the execution context of a content script is destroyed when a Firefox extension is disabled, before the port.onDisconnect event has a chance to fire (at least, until bugzil.la/1223425 is fixed).

Despite these constraints, it is still possible to run clean up logic for a content script when an add-on is disabled. This method is based on the fact that in Firefox, style sheets inserted with tabs.insertCSS are removed when an add-on is disabled.

I will discuss two ways to exploit this characteristic. The first method allows execution of arbitrary code. The second method does not provide execution of arbitrary code, but it is simpler and sufficient if you only want to hide some extension-inserted DOM elements.

Method 1: Run code in page when extension is disabled

One of the ways to observe style changes is by declaring CSS transitions and using transition events to detect CSS property changes.
For this to be helpful, you need to construct a style sheet in such a way that it only affects your HTML elements. So you need to generate a unique selector (class name, ID, ...) and use that for your HTML element(s) and style sheet.

This is code that you have to put in your background script:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message !== 'getStyleCanary') return;

    // Generate a random class name, insert a style sheet and send
    // the class back to the caller if successful.
    var CANARY_CLASS = '_' + crypto.getRandomValues(new Uint32Array(2)).join('');
    var code = '.' + CANARY_CLASS + ' { opacity: 0 !important; }';
    chrome.tabs.insertCSS(sender.tab.id, {
        code,
        frameId: sender.frameId,
        runAt: 'document_start',
    }, function() {
        if (chrome.runtime.lastError) {
            // Failed to inject. Frame unloaded?
            sendResponse();
        } else {
            sendResponse(CANARY_CLASS);
        }
    });
    return true; // We will asynchronously call sendResponse.
});

In the content script:

chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // NOTE: Consider retry sending the message in case
        // the background page was not ready yet.
        return;
    }

    var s = document.createElement('script');
    s.src = chrome.runtime.getURL('canaryscript.js');
    s.onload = s.remove;
    s.dataset.canaryClass = CANARY_CLASS;

    // This function will become available to the page and be used
    // by canaryscript.js. NOTE: exportFunction is Firefox-only.
    exportFunction(function() {}, s, {defineAs: 'checkCanary'});

    (document.body || document.documentElement).appendChild(s);
});

I use a script tag above, because it is the only way to run a script in the page without being blocked by the page's content security policy. Make sure that you add canaryscript.js to web_accessible_resources in manifest.json, or else the script won't load.

If running the cleanup code is not critical (e.g. because you also use method 2 which I explain later), then you should preferably use inline scripts instead of external scripts (i.e. use s.textContent = '<content of canaryscript.js>' instead of s.src = ...). This is because using .src with extension resources introduces a fingerprinting vulnerability to Firefox (bug 1372288).

This is the content of canaryscript.js:

(function() {
    // Thes two properties are set in the content script.
    var checkCanary = document.currentScript.checkCanary;
    var CANARY_CLASS = document.currentScript.dataset.canaryClass;

    var canary = document.createElement('span');
    canary.className = CANARY_CLASS;
    // The inserted style sheet has opacity:0. Upon removal a transition occurs.
    canary.style.opacity = '1';
    canary.style.transitionProperty = 'opacity';
    // Wait a short while to make sure that the content script destruction
    // finishes before the style sheet is removed.
    canary.style.transitionDelay = '100ms';
    canary.style.transitionDuration = '1ms';
    canary.addEventListener('transitionstart', function() {
       // To avoid inadvertently running clean-up logic when the event
       // is triggered by other means, check whether the content script
       // was really destroyed.
       try {
            // checkCanary will throw if the content script was destroyed.
            checkCanary();
            // If we got here, the content script is still valid.
            return;
        } catch (e) {
        }
        canary.remove();

        // TODO: Put the rest of your clean up code here.
    });
    (document.body || document.documentElement).appendChild(canary);
})();

Note: CSS transition events are only fired if the tab is active. If the tab is inactive, the transition event will not fire until the tab is shown.

Note: exportFunction is a Firefox-only extension method to define a function in a different execution context (in the above example, the function was defined in the page's context, available to scripts running in that page).

All other APIs are available in other browsers too (Chrome/Opera/Edge), but the code cannot be used to detect disabled extensions, because style sheets from tabs.insertCSS are not removed upin uninstall (I only tested with Chrome; it might work in Edge).

Method 2: Visual restoration upon uninstallation

Method 1 allows you to run arbitrary code, such as removing all elements that you inserted in the page. As an alternative to removing the elements from the DOM, you can also choose to hide the elements through CSS.

Below I show how you can modify method 1 to hide the elements without running other code (such as canaryscript.js).

When your content script creates an element for insertion in the DOM, you hide it with an inline style:

var someUI = document.createElement('div');
someUI.style.display = 'none'; // <-- Hidden
// CANARY_CLASS is the random class (prefix) from the background page.
someUI.classList.add(CANARY_CLASS + 'block');
// ... other custom logic, and add to document.

In the style sheet that you add with tabs.insertCSS, you then define the desired display value, with the !important flag so that the inline style is overridden:

// Put this snippet after "var code = '.' + CANARY_CLASS, above.
code += '.' + CANARY_CLASS + 'block {display: block !important;}';

The above example is intentionally generic. If you have multiple UI elements with different CSS display values (e.g. block, inline, ...), then you can add multiple of these lines to re-use the framework that I provided.

To show the simplicity of method 2 over method 1: you can use the same background script (with the above modification), and use the following in the content script:

// Example: Some UI in the content script that you want to clean up.
var someUI = document.createElement('div');
someUI.textContent = 'Example: This is a test';
document.body.appendChild(someUI);

// Clean-up is optional and a best-effort attempt.
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // Do not add clean-up classes.
        return;
    }
    someUI.classList.add(CANARY_CLASS + 'block');
    someUI.style.display = 'none';
});

If your extension has more than one element, consider caching the value of CANARY_CLASS in a local variable so that you only insert one new style sheet per execution context.


来源: stackexchange

问:
Processing Checkbox Group into String
I have a form which contains a series of checkboxes, in a group. Here is the HTML -

```<code><input type="checkbox" name="p_contents[]" value="Value 1" />
<input type="checkbox" name="p_contents[]" value="Value 2" />
<input type="checkbox" name="p_contents[]" value="Value 3" />
<input type="checkbox" name="p_contents[]" value="Value 4" />
<input type="checkbox" name="p_contents[]" value="Value 5" />
</code>```

And I wish to process this with javascript -

```<code>var contents;
for(var i=0; i < document.forms['addForm'].elements['p_contents[]'].length; i++){
    if(i != (document.forms['addForm'].elements['p_contents[]'].length - 1)){
        if(document.forms['addForm'].elements['p_contents[]'].checked){
            contents += encodeURIComponent(document.forms['addForm'].elements['p_contents[]'][i].value) + ",";
        }
    }else{
        if(document.forms['addForm'].elements['p_contents[]'][i].checked){
            contents += encodeURIComponent(document.forms['addForm'].elements['p_contents[]'][i].value);
        }
    }
}
</code>```

I'm sure the problem is with the enumeration:

```<code>document.forms['addForm'].elements['p_contents[]'][i].checked
</code>```

Is this the correct way to process a grouped checkbox form?

答:
As specified by @9000 you can do the same by adding elements id and iterating it following way</p>

```<code>for (i = 0; i < length; i++) { //you can calculate length as you did before or any alternate way
    var prefix = 'yourPrefixToId';
    if (document.getElementById(prefix + i).checked) {
    // do stuff
    }
}
</code>```

ContentLicense: CC BY-SA 2.5

Title: Processing Checkbox Group into String

Body:
I have a form which contains a series of checkboxes, in a group. Here is the HTML -

<input type="checkbox" name="p_contents[]" value="Value 1" />
<input type="checkbox" name="p_contents[]" value="Value 2" />
<input type="checkbox" name="p_contents[]" value="Value 3" />
<input type="checkbox" name="p_contents[]" value="Value 4" />
<input type="checkbox" name="p_contents[]" value="Value 5" />

And I wish to process this with javascript -

var contents;
for(var i=0; i < document.forms['addForm'].elements['p_contents[]'].length; i++){
    if(i != (document.forms['addForm'].elements['p_contents[]'].length - 1)){
        if(document.forms['addForm'].elements['p_contents[]'].checked){
            contents += encodeURIComponent(document.forms['addForm'].elements['p_contents[]'][i].value) + ",";
        }
    }else{
        if(document.forms['addForm'].elements['p_contents[]'][i].checked){
            contents += encodeURIComponent(document.forms['addForm'].elements['p_contents[]'][i].value);
        }
    }
}

I'm sure the problem is with the enumeration:

document.forms['addForm'].elements['p_contents[]'][i].checked

Is this the correct way to process a grouped checkbox form?

Body:
As specified by @9000 you can do the same by adding elements id and iterating it following way

for (i = 0; i < length; i++) { //you can calculate length as you did before or any alternate way
    var prefix = 'yourPrefixToId';
    if (document.getElementById(prefix + i).checked) {
    // do stuff
    }
}


问:
Request header field Authorization is not allowed by Access-Control-Allow-Headers Error
I have a VueJS application and a ExpressJS server side application. On my server I have the following:

```<code>var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    next();
}
app.use(allowCrossDomain);
</code>```

However, I keep getting the following error:

Request header field Authorization is not allowed by
  Access-Control-Allow-Headers in preflight response.

I'm sending my request like this:

```<code>import axios from 'axios'

export default axios.create({
  baseURL: 'https://example.com',
  timeout: 5000,
  headers: {
    'Authorization': 'Bearer ' + accessToken
  }
})
</code>```

How can I get around this? I have tried many solutions and read up on this. I have read the below:

How to allow CORS?

CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response

Request header field Access-Control-Allow-Headers is not allowed by itself in preflight response

答:
The code in the question apparently isn’t causing the Access-Control-Allow-Headers header to get sent for OPTIONS responses — specifically, for the response to the CORS preflight OPTIONS.

To ensure preflight OPTIONS get handled correctly, consider installing the npm cors package:

```<code>npm install cors
</code>```

And then do something like this:

```<code>var express = require('express')
  , cors = require('cors')
  , app = express();
app.options('*', cors()); // preflight OPTIONS; put before other routes
</code>```

That will handle CORS preflight OPTIONS requests and other CORS aspects without you needing to manually write your own handling from scratch in your application code.

https://www.npmjs.com/package/cors#configuration-option has more details on all the  options.

ContentLicense: CC BY-SA 3.0

Body:
I have a VueJS application and a ExpressJS server side application. On my server I have the following:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    next();
}
app.use(allowCrossDomain);

However, I keep getting the following error:

Request header field Authorization is not allowed by
  Access-Control-Allow-Headers in preflight response.

I'm sending my request like this:

import axios from 'axios'

export default axios.create({
  baseURL: 'https://example.com',
  timeout: 5000,
  headers: {
    'Authorization': 'Bearer ' + accessToken
  }
})

How can I get around this? I have tried many solutions and read up on this. I have read the below:

How to allow CORS?

CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response

Request header field Access-Control-Allow-Headers is not allowed by itself in preflight response

Body:
The code in the question apparently isn’t causing the Access-Control-Allow-Headers header to get sent for OPTIONS responses — specifically, for the response to the CORS preflight OPTIONS.

To ensure preflight OPTIONS get handled correctly, consider installing the npm cors package:

npm install cors

And then do something like this:

var express = require('express')
  , cors = require('cors')
  , app = express();
app.options('*', cors()); // preflight OPTIONS; put before other routes

That will handle CORS preflight OPTIONS requests and other CORS aspects without you needing to manually write your own handling from scratch in your application code.

https://www.npmjs.com/package/cors#configuration-option has more details on all the  options.


问:
What is wrong with my Model and Interface? (Java Spring Boot webapp)
I have an error during running that says (with full directories omitted for brevity:

Error creating bean with name 'idea_Service' defined in file
  [...com\vincentsnow\brightideas\services\Idea_Service.class]:
  Unsatisfied dependency expressed through constructor parameter 0;
  nested exception is
  org.springframework.beans.factory.BeanCreationException: Error
  creating bean with name 'idea_Repository': Invocation of init method
  failed; nested exception is java.lang.IllegalArgumentException: Not a
  managed type: class java.lang.Object

and another:

Error creating bean with name 'idea_Repository': Invocation of init
  method failed; nested exception is java.lang.IllegalArgumentException:
  Not a managed type: class java.lang.Object

I haven't used these lowercase names anywhere. I can't make sense of this message...I'm guessing it's trying to compile but changing the filenames for some reason. No errors are caught by STS in my files. Here they are:

Idea_Service.java (service)

```<code>    package com.vincentsnow.brightideas.services;

import java.util.List;
import org.springframework.stereotype.Service;
import com.vincentsnow.brightideas.models.Idea;
import com.vincentsnow.brightideas.models.User;
import com.vincentsnow.brightideas.repositories.Idea_Repository;

@Service
public class Idea_Service {
    private Idea_Repository ideaRepository;
    public Idea_Service(Idea_Repository ideaRepository){
        this.ideaRepository = ideaRepository;
    }

    public List<Idea> allIdeas(){
        return ideaRepository.findAllIdeas();
    }

    public void createUser(String idea, User posted_by){
        ideaRepository.save(idea, posted_by);
    }

    public Idea oneIdea(Long id) {
        return ideaRepository.getSingleIdeaWhereId(id);
    }

    public List<Idea> likesof(Long id){
        return ideaRepository.getLikesOfIdea(id);
    }
}
</code>```

Idea.java (model)

```<code>package com.vincentsnow.brightideas.models;

import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.validation.constraints.Size;
import com.vincentsnow.brightideas.models.User;

@Entity
public class Idea {

        @Id
        @GeneratedValue
        private Long id;

        @Column
        @Size(min=3, max=5000)
        private String idea;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name="idea_id")
        private User posted_by;

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(
            name = "likes",
            joinColumns = @JoinColumn(name = "idea_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id")
            )
            private List<Idea> likes;

}
</code>```

Idea_Repository.java (repository)

```<code>package com.vincentsnow.brightideas.repositories;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.vincentsnow.brightideas.models.Idea;
import com.vincentsnow.brightideas.models.User;

@Repository
public interface Idea_Repository extends CrudRepository {

    @Query("SELECT a FROM ideas a")
    List<Idea> findAllIdeas();

    @Query(value="INSERT INTO ideas (idea, posted_by) VALUES (?1, ?2, ?3, ?4)", nativeQuery=true)
    Idea save(String idea, User posted_by);

    @Query("SELECT a FROM ideas a WHERE id=?1")
    Idea getSingleIdeaWhereId(Long id);

    @Query(value="SELECT likes WHERE idea_id = ?1", nativeQuery=true)
    List<Idea> getLikesOfIdea(Long id);

}
</code>```

What is wrong with them? What does this error mean? What is STS trying to do?

答:
CrudRepository takes two formal parameters, being the type that it manages and the type of that type's ID. Since you haven't provided these parameters, they are effectively Object and Object: the error message is pointing out that Object isn't a manageable object (it's not annotated with @Entity). You need to tell Spring Data that the class your repository is actually trying to manage is Idea. Try:

```<code>public interface Idea_Repository extends CrudRepository<Idea, Long> {
    // ..
}
</code>```

Also, you don't need findAllIdeas, save or getSingleIdeaWhereId; equivalent methods are provided by Spring Data JPA for free.

Body:
I have an error during running that says (with full directories omitted for brevity:

Error creating bean with name 'idea_Service' defined in file
  [...com\vincentsnow\brightideas\services\Idea_Service.class]:
  Unsatisfied dependency expressed through constructor parameter 0;
  nested exception is
  org.springframework.beans.factory.BeanCreationException: Error
  creating bean with name 'idea_Repository': Invocation of init method
  failed; nested exception is java.lang.IllegalArgumentException: Not a
  managed type: class java.lang.Object

and another:

Error creating bean with name 'idea_Repository': Invocation of init
  method failed; nested exception is java.lang.IllegalArgumentException:
  Not a managed type: class java.lang.Object

I haven't used these lowercase names anywhere. I can't make sense of this message...I'm guessing it's trying to compile but changing the filenames for some reason. No errors are caught by STS in my files. Here they are:

Idea_Service.java (service)

    package com.vincentsnow.brightideas.services;

import java.util.List;
import org.springframework.stereotype.Service;
import com.vincentsnow.brightideas.models.Idea;
import com.vincentsnow.brightideas.models.User;
import com.vincentsnow.brightideas.repositories.Idea_Repository;

@Service
public class Idea_Service {
    private Idea_Repository ideaRepository;
    public Idea_Service(Idea_Repository ideaRepository){
        this.ideaRepository = ideaRepository;
    }

    public List<Idea> allIdeas(){
        return ideaRepository.findAllIdeas();
    }

    public void createUser(String idea, User posted_by){
        ideaRepository.save(idea, posted_by);
    }

    public Idea oneIdea(Long id) {
        return ideaRepository.getSingleIdeaWhereId(id);
    }

    public List<Idea> likesof(Long id){
        return ideaRepository.getLikesOfIdea(id);
    }
}

Idea.java (model)

package com.vincentsnow.brightideas.models;

import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.validation.constraints.Size;
import com.vincentsnow.brightideas.models.User;

@Entity
public class Idea {

        @Id
        @GeneratedValue
        private Long id;

        @Column
        @Size(min=3, max=5000)
        private String idea;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name="idea_id")
        private User posted_by;

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(
            name = "likes",
            joinColumns = @JoinColumn(name = "idea_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id")
            )
            private List<Idea> likes;

}

Idea_Repository.java (repository)

package com.vincentsnow.brightideas.repositories;

import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.vincentsnow.brightideas.models.Idea;
import com.vincentsnow.brightideas.models.User;

@Repository
public interface Idea_Repository extends CrudRepository {

    @Query("SELECT a FROM ideas a")
    List<Idea> findAllIdeas();

    @Query(value="INSERT INTO ideas (idea, posted_by) VALUES (?1, ?2, ?3, ?4)", nativeQuery=true)
    Idea save(String idea, User posted_by);

    @Query("SELECT a FROM ideas a WHERE id=?1")
    Idea getSingleIdeaWhereId(Long id);

    @Query(value="SELECT likes WHERE idea_id = ?1", nativeQuery=true)
    List<Idea> getLikesOfIdea(Long id);

}

What is wrong with them? What does this error mean? What is STS trying to do?

Body:
CrudRepository takes two formal parameters, being the type that it manages and the type of that type's ID. Since you haven't provided these parameters, they are effectively Object and Object: the error message is pointing out that Object isn't a manageable object (it's not annotated with @Entity). You need to tell Spring Data that the class your repository is actually trying to manage is Idea. Try:

public interface Idea_Repository extends CrudRepository<Idea, Long> {
    // ..
}

Also, you don't need findAllIdeas, save or getSingleIdeaWhereId; equivalent methods are provided by Spring Data JPA for free.


问:
Interview Question: Replacing two arrays's place in memory
Given two consecutive arrays, A and B. They look something like

```<code>int AandB[] = {a1,a2,...,am,b1,b2,...,bn};
</code>```

You need to write a program that would switch the order of arrays A and B in the memory, so that B would appear before A. In our example, AandB should become

```<code>int AandB[] = {b1,b2,...,bn,a1,...,am};
</code>```

What's the most efficient way to do that?

答:
we can use array_merge from php.

use array_splice() first to split these arrays and then use the above function. This is for php.

Body:
Given two consecutive arrays, A and B. They look something like

int AandB[] = {a1,a2,...,am,b1,b2,...,bn};

You need to write a program that would switch the order of arrays A and B in the memory, so that B would appear before A. In our example, AandB should become

int AandB[] = {b1,b2,...,bn,a1,...,am};

What's the most efficient way to do that?


问:
Create a custom drop down menu and place dataset value in input
I'm trying to create a custom drop down menu, using HTML, CSS and Vanilla Javascript.

I've managed to get the menu to appear when the user clicks on the the "from" input field, however when I try and click on an option, it wont let you add the value stored in the "code" dataset.

I did get it to work by using setTimeout method, however it is a bit hit and miss sometimes and doesn't seem like a good solution.

Is there an alternative way to get it to work?

function app() {

  var messages = document.querySelector(".messages");

  var inputFrom = document.querySelector(".input-from");

  var inputTo = document.querySelector(".input-to");

  var nearestContainer = document.querySelector(".nearest-container");

  inputFrom.addEventListener("focus", inputToFocusIn, false);

  function inputToFocusIn(e) {

    messages.innerHTML = "focusin event triggered on input-from";

    // add class
    inputFrom.classList.add("input-from--focusin");
    nearestContainer.classList.add("nearest-container--active");

    // remove class
    inputFrom.classList.remove("input-from--focusout");
    nearestContainer.classList.remove("nearest-container--hidden");

  }

  inputFrom.addEventListener("focusout", inputToFocusOut, false);

  function inputToFocusOut(e) {

    messages.innerHTML = "focusout event triggered on input-from";

      // add class
      inputFrom.classList.remove("input-from--focusin");
      nearestContainer.classList.remove("nearest-container--active");

      // remove class
      inputFrom.classList.add("input-from--focusout");
      nearestContainer.classList.add("nearest-container--hidden");

  }

  var nearestStations = document.querySelectorAll(".nearest-station");

  // add event listener to buttons
  for(var nearestStation of nearestStations) {

    nearestStation.addEventListener("click", addToInputFrom, false);

  }

  function addToInputFrom(e) {

    inputFrom.classList.add("input-from--focusout");
    nearestContainer.classList.add("nearest-container--hidden");

    inputFrom.classList.remove("input-from--focusin")
    nearestContainer.classList.remove("nearest-container--active")

    var targetDataset = e.currentTarget.dataset.code;

    messages.innerHTML = "station added to input from field"

    inputFrom.value = "";
    inputFrom.value = targetDataset;

  }

  var switchButton = document.querySelector(".button-switch");

  switchButton.addEventListener("click", clickSwitch, false);

  function clickSwitch(e) {

    var inputFromValue = inputFrom.value;
    var inputToValue = inputTo.value;

    inputFrom.value = inputToValue;
    inputTo.value = inputFromValue;

  }

}

window.onload = app();```
<pre class="snippet-code-css lang-css prettyprint-override"><code>/* stylesheet */

body {
 font-family: "GRAPHIK";
 font-style: normal;
 font-weight: 400;
 font-size: 16px;
 color: #242424;
}

* {
 box-sizing: border-box;
 outline: none;
}

.container {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 width: 100vw;
 height: 100vh;
 background-color: #FF4136;
}

.search-container {
 display: flex;
 flex-direction: column;
 flex-shrink: 0;
 width: 300px;
 padding: 10px;
 background-color: #FFF;
 border-radius: 10px;
}

.form-container {
 display: flex;
 flex-direction: row;
 width: 100%;
}

.input-container {
 width: 100%;
}

.input {
 width: 100%;
 border: none;
 border-radius: 10px;
 background-color: #f1f1f1;
 padding: 10px;
}

.input-from {
 margin-bottom: 5px;
}

.input-from--focusout {
 border-radius: 10px;
}

.input-from--focusin {
 border-radius: 10px 10px 0 0;
}

.input-to {
 margin-bottom: 5px;
}

.switch-container {
  margin-bottom: 5px;
}

.button {
 border: none;
 background-color: transparent;
}

.button-switch {
 height: 100%;
 width: 38px;
 margin-left: 5px;
 background-color: #f1f1f1;
 border-radius: 10px;
 background-image: url(../assets/images/switch.svg);
 background-position: center;
 background-size: 20px;
 background-repeat: no-repeat;
}

.button-switch:hover {
 background-image: url(../assets/images/switch-hover.svg);
}

.button-search {
 padding: 10px;
 background-color: #2ECC40;
 color: #FFF;
 border-radius: 10px;
 width: 100%;
 transition: background-color 0.5s ease;
}

.button-search:hover {
 background-color: #33e147;
}

.input-container-to {
  position: relative;

}

.nearest-container {
 position: absolute;
 top: 38px;
 background-color: #f1f1f1;
 padding: 5px;
 border-radius: 0 0 10px 10px;
 width: 100%;
 z-index: 100;
}

.messages {
 width: 300px;
 background-color: #FFF;
 padding: 5px;
 border-radius: 10px;
 text-align: center;
 margin-bottom: 5px;
 font-size: 10px;
}

.finding, .show-more {
 width: 100%;
 font-size: 10px;
 font-style: italic;
 margin: 0;
 padding: 5px;
}

.show-more {
 text-align: center;
}

.nearest-station {
 font-size: 10px;
 padding: 5px;
 border-radius: 10px;
}

.nearest-container--hidden {
 display: none;
}

.nearest-station--active {
 display: flex;
}

.nearest-station:hover {
 background-color: #FFF;
 cursor: pointer;
}

.logo {
 margin-right: 5px;
}

.nr-logo {
 width: 15px;
}

.station-distance {
 font-style: italic;
 float: right;
}</code>```

messages here

      <input type="text" class="input input-from" placeholder="From">

Finding stations closest to you...

         Leigh-on-Sea
         0.6km

         Chalkwell
         1.5km

         Westcliff
         2.7km

         Southend Central
         3.6km

         Southend Victoria
         3.8km

      <input type="text" class="input input-to" placeholder="To">

     <input type="button" class="button button-switch">

    <input type="button" class="button button-search" value="Search">

```

答:
Using focusout event here

inputFrom.addEventListener("focusout", inputToFocusOut, false);

is not right. Because it will be triggered before click event.
When the function inputToFocusOut is executed the .nearest-container becomes hidden:

nearestContainer.classList.add("nearest-container--hidden");

and that's why click event for it and all of its child nodes (we are interested in .nearest-station elements) won't be triggered. Instead of focusout, use mousedown event. With blur event it won't work.

Body:
I'm trying to create a custom drop down menu, using HTML, CSS and Vanilla Javascript.

I've managed to get the menu to appear when the user clicks on the the "from" input field, however when I try and click on an option, it wont let you add the value stored in the "code" dataset.

I did get it to work by using setTimeout method, however it is a bit hit and miss sometimes and doesn't seem like a good solution.

Is there an alternative way to get it to work?

function app() {

  var messages = document.querySelector(".messages");

  var inputFrom = document.querySelector(".input-from");

  var inputTo = document.querySelector(".input-to");

  var nearestContainer = document.querySelector(".nearest-container");

  inputFrom.addEventListener("focus", inputToFocusIn, false);

  function inputToFocusIn(e) {

    messages.innerHTML = "focusin event triggered on input-from";

    // add class
    inputFrom.classList.add("input-from--focusin");
    nearestContainer.classList.add("nearest-container--active");

    // remove class
    inputFrom.classList.remove("input-from--focusout");
    nearestContainer.classList.remove("nearest-container--hidden");

  }

  inputFrom.addEventListener("focusout", inputToFocusOut, false);

  function inputToFocusOut(e) {

    messages.innerHTML = "focusout event triggered on input-from";

      // add class
      inputFrom.classList.remove("input-from--focusin");
      nearestContainer.classList.remove("nearest-container--active");

      // remove class
      inputFrom.classList.add("input-from--focusout");
      nearestContainer.classList.add("nearest-container--hidden");

  }

  var nearestStations = document.querySelectorAll(".nearest-station");

  // add event listener to buttons
  for(var nearestStation of nearestStations) {

    nearestStation.addEventListener("click", addToInputFrom, false);

  }

  function addToInputFrom(e) {

    inputFrom.classList.add("input-from--focusout");
    nearestContainer.classList.add("nearest-container--hidden");

    inputFrom.classList.remove("input-from--focusin")
    nearestContainer.classList.remove("nearest-container--active")

    var targetDataset = e.currentTarget.dataset.code;

    messages.innerHTML = "station added to input from field"

    inputFrom.value = "";
    inputFrom.value = targetDataset;

  }

  var switchButton = document.querySelector(".button-switch");

  switchButton.addEventListener("click", clickSwitch, false);

  function clickSwitch(e) {

    var inputFromValue = inputFrom.value;
    var inputToValue = inputTo.value;

    inputFrom.value = inputToValue;
    inputTo.value = inputFromValue;

  }

}

window.onload = app();

/* stylesheet */

body {
 font-family: "GRAPHIK";
 font-style: normal;
 font-weight: 400;
 font-size: 16px;
 color: #242424;
}

* {
 box-sizing: border-box;
 outline: none;
}

.container {
 display: flex;
 flex-direction: column;
 justify-content: center;
 align-items: center;
 width: 100vw;
 height: 100vh;
 background-color: #FF4136;
}

.search-container {
 display: flex;
 flex-direction: column;
 flex-shrink: 0;
 width: 300px;
 padding: 10px;
 background-color: #FFF;
 border-radius: 10px;
}

.form-container {
 display: flex;
 flex-direction: row;
 width: 100%;
}

.input-container {
 width: 100%;
}

.input {
 width: 100%;
 border: none;
 border-radius: 10px;
 background-color: #f1f1f1;
 padding: 10px;
}

.input-from {
 margin-bottom: 5px;
}

.input-from--focusout {
 border-radius: 10px;
}

.input-from--focusin {
 border-radius: 10px 10px 0 0;
}

.input-to {
 margin-bottom: 5px;
}

.switch-container {
  margin-bottom: 5px;
}

.button {
 border: none;
 background-color: transparent;
}

.button-switch {
 height: 100%;
 width: 38px;
 margin-left: 5px;
 background-color: #f1f1f1;
 border-radius: 10px;
 background-image: url(../assets/images/switch.svg);
 background-position: center;
 background-size: 20px;
 background-repeat: no-repeat;
}

.button-switch:hover {
 background-image: url(../assets/images/switch-hover.svg);
}

.button-search {
 padding: 10px;
 background-color: #2ECC40;
 color: #FFF;
 border-radius: 10px;
 width: 100%;
 transition: background-color 0.5s ease;
}

.button-search:hover {
 background-color: #33e147;
}

.input-container-to {
  position: relative;

}

.nearest-container {
 position: absolute;
 top: 38px;
 background-color: #f1f1f1;
 padding: 5px;
 border-radius: 0 0 10px 10px;
 width: 100%;
 z-index: 100;
}

.messages {
 width: 300px;
 background-color: #FFF;
 padding: 5px;
 border-radius: 10px;
 text-align: center;
 margin-bottom: 5px;
 font-size: 10px;
}

.finding, .show-more {
 width: 100%;
 font-size: 10px;
 font-style: italic;
 margin: 0;
 padding: 5px;
}

.show-more {
 text-align: center;
}

.nearest-station {
 font-size: 10px;
 padding: 5px;
 border-radius: 10px;
}

.nearest-container--hidden {
 display: none;
}

.nearest-station--active {
 display: flex;
}

.nearest-station:hover {
 background-color: #FFF;
 cursor: pointer;
}

.logo {
 margin-right: 5px;
}

.nr-logo {
 width: 15px;
}

.station-distance {
 font-style: italic;
 float: right;
}

messages here

      <input type="text" class="input input-from" placeholder="From">

Finding stations closest to you...

         Leigh-on-Sea
         0.6km

         Chalkwell
         1.5km

         Westcliff
         2.7km

         Southend Central
         3.6km

         Southend Victoria
         3.8km

      <input type="text" class="input input-to" placeholder="To">

     <input type="button" class="button button-switch">

    <input type="button" class="button button-search" value="Search">