Express/Nodemailer – send info to browser after sending email

I’m vary new to express and I think I’m struggling with something what is not that hard…

I have a function sendEmail

exports.sendEmail = async (req, res) => {
  const transport = nodemailer.createTransport({
    host: process.env.MAIL_HOST,
    port: process.env.MAIL_PORT,
    secure: false,
    auth: {
      user: process.env.MAIL_USER,
      pass: process.env.MAIL_PASSWORD,
    },
    tls: {
      rejectUnauthorized: false,
    },
  });

  await transport.sendMail(
    { body },
    (err, info) => {
      console.log(err);
      console.log(info);
// here is a problem
          if (err) {
           res.status(200).json({ msg: 'error' });
           throw err;
           } else {
             res.status(200).json({ msg: 'mesage  
             has been sent' });
           }
          ); 

This function works fine – email is send. But what I want to achieve is send info to browser with info ‘is sent’ or ‘error’.

In frontend I have this code:

await axios.all([
      axios.post(`${process.env.REACT_APP_DOMAIN}/api/send-email`, {
        data,
      }),
     
      axios
        .get(`${process.env.REACT_APP_DOMAIN}/api/send-email`)
        .then((res) => {
          console.log(res);
        }),
    ]);

axios.post works fine, but axios.get is not. The response I get doesn’t contain data I want.

Could anyone explain me how to send data I need to after email is sent?

Thanks.

How can I decrease a key of a state in React without knowing the key name? [duplicate]

I have the following state in React:

const [counter, setCounter] = useState({foo: 1, bar: 1});

and I have data like

const data = [{who: "foo", otherVal: "bla"}, {who: "bar", otherVal: "bla"}];

If I remove an object from the data array, I want to decrease the counter by 1 related to the who value of the deleted object.

If {who: "foo", otherVal: "bla"} gets removed, I want to decrease the foo key of the counter because who === "foo". But In my app I don’t know that foo is foo or bar is bar. I must use counter[data.who] somehow to update my state.

clearRect clearing the entire screen

i’m new to js and would like some help. I’m making a system where I’m taking the value of an input and inserting the typed text inside an image with canvas. I would like some help because when I have more than one input field, when using ctx.clearRect, it always clears the contents of the other input when typing the second input. Could someone help me?

My form

<form>
    <img style="display:none" src="images/facebook.jpg" crossorigin="anonymous" />
    <input type="text" id="vaga" />
    <input type="text" id="nome" />
    <button type="submit">Save</button>
</form>

My JS

    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d');
    canvas.width = $('img').width();
    canvas.crossOrigin = "Anonymous";
    canvas.height = $('img').height();
    ctx.drawImage($('img').get(0), 0, 0);
    ctx.font = "36pt Verdana";

    $(document).on('input', '#vaga', function() {
        //redraw image
        ctx.clearRect(550, 80, 200, 20);
        ctx.drawImage($('img').get(0), 0, 0);
        //refill text
        ctx.font = "26pt 'Bebas Neue'";
        ctx.fillStyle = "black";
        ctx.textAlign = 'center';
        //ctx.fillText('center-aligned', x, 85);
        ctx.fillText($(this).val(), 550, 80);
    });

    $(document).on('input', '#nome', function() {
        //redraw image
        //ctx.clearRect(190, 180, 120, 120);
        ctx.drawImage($('img').get(0), 0, 0);
        //refill text
        ctx.font = "14pt 'Bebas Neue 400'";
        ctx.textAlign = 'left';
        ctx.fillStyle = "black";
        ctx.fillText($(this).val(), 190, 180);
    });

    $('button').click(function() {
        console.log(ctx.getImageData(50, 50, 100, 100));
    });

Thaks

Chrome Extension Create a Tab and inject content into it

Hi I’m trying to create a new tab in my background script, and inject a content script (for now its just a regular alert to check the code), but I cant manage to do that since
Content scripts cant be injected into nealy created tabs that follow the chrome-extensions:// scheme and the CSP blocks me if I try to use inline script.

So to conclude:
I’d like to create a new tab from a background script and inject content script into the newly created tab.

my manifest:

{
    "manifest_version": 3,
    "name": "OSINTisizer",
    "description": "OSINT any IP or URL",
    "version": "1.20",
    "icons": {
        "48": "images/logo_48.png",
        "128": "images/logo_128.png"
    },
    "background": {
        "service_worker": "background.js"
    },
    "content_scripts": [
        {
          "matches": ["https://*/report.html"],
          "js": ["report.js"]
        }
      ],
    "permissions": [
        "contextMenus",
        "background",
        "scripting",
        "tabs",
        "storage"
    ],
    "host_permissions": [
        "*://*/*"
    ]
}

My background.js code snippet that is relevant:

chrome.storage.local.set({ info: uniqeEmails }, function () {});
          chrome.tabs.create({ url: "report.html" });
        });
      });

moongose query for finding qunique document

json : {
       data:{
            base:"Id",
           },
        res:{
           message:"sddg"
           }
     }

i want to find if a particular record already exist on the basis of base unique attribute using moongose in mogodb database, but unable to do so.
my approach is

model.findOne({data:{base:”Id”});

Fetch api call in onclick event triggering every alternate run and not every time

I am calling the fetch api call on a onclick event of the submit button after upload change event is performed… I observed that the fetch is not working every alternate hit but the function loadperson( in which fetch is called) is working as the alert in it is showing.
on every alternate hit it sends the payload as expected.
also while running in debug mode its running fine on every hit and payload is being sent perfectly.

is there any reasonsolution to the scenario.

for example :–

first run -no api hit….. second payload – api hit sucessfull …..third run -no api hit….. fourth payload – api hit sucessfull

<!DOCTYPE html>
<html lang="en">

<head>
    <title>File upload</title>
</head>

<body>
    <form id="Form">
        <input type="file" />
        <button type="submit" id="smtbtn" onclick = "hj()" >Submit</button>
    </form>
    <script>
        var base64;
        function loadPersons(a) {
//alert("submit started");
            fetch("http://localhost:8081/api", {
mode: "no-cors",
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    "data": a
                })
            });
alert("submit successful.");
        };
function hj() {
loadPersons(base64)
};
        const fileInput = document.querySelector("input");
        fileInput.addEventListener("change", (e) => {
            const file = e.target.files[0];
            const reader = new FileReader();
            reader.onloadend = () => {
                console.log(reader.result);
                base64 = reader.result;
            };
            reader.readAsDataURL(file);
        });
    </script>
</body>

</html>

How to disable error/warning overlay in webpack-dev-server 3.11.2

I want to disable the error overlay in development but it seems that configuration overlay: false is ignored with webpack-dev-server@3.11.2

Reproduction

serverConfig here configuration set with overlay: false :

{
  disableHostCheck: true,
  compress: true,
  clientLogLevel: 'none',
  contentBase: '/home/dka/workspace/github.com/pass-culture/pass-culture-app-native/public',
  contentBasePublicPath: '/',
  watchContentBase: true,
  hot: true,
  transportMode: 'ws',
  injectClient: false,
  sockHost: undefined,
  sockPath: undefined,
  sockPort: undefined,
  publicPath: '',
  quiet: true,
  watchOptions: {
    ignored: /^(?!/home/dka/workspace/github.com/pass-culture/pass-culture-app-native/src/).+/node_modules//g
  },
  https: false,
  host: '0.0.0.0',
  overlay: false,
  historyApiFallback: { disableDotRule: true, index: '/' },
  public: '192.168.1.102',
  proxy: {
    '/native': {
      target: 'https://backend.testing.passculture.team',
      changeOrigin: true
    },
    '/saml': {
      target: 'https://backend.testing.passculture.team',
      changeOrigin: true
    }
  },
  before: [Function: before],
  after: [Function: after]
}

Expected Behavior

It should disable the overlay as explained here and here

Seems related to https://github.com/electron-userland/electron-forge/issues/2413 / https://github.com/webpack/webpack-dev-server/issues/4111

Debugging JavaScript Uncaught SyntaxError: Unexpected token ‘;’ [closed]

I have a problem with the last line. Does someone know how to fix this code?
The “console” says: Uncaught SyntaxError: Unexpected token ‘;’.
Thanks

var userDatabase = [
    {
        username: "Admin",
        password: "4444",
    }
];

var newsfeed = [
    {
        username: "Max",
        timeline: "Good Morning!",
    },
    {
        username: "Alex",
        timeline: "Have a cool day!",
    }
];

var signUpName = prompt("Enter Username");
var signUpPassword = prompt("Enter Password");

function login(a, b) {
    if (a === userDatabase[0].username && b === userDatabase[0].password) {
        console.log(newsfeed);
        } else {
            alert("Wrong Details");
        }
}
    
function login(signUpName, signUpPassword);

How to run flask server as portable elf

Hello i am working on a small project using electron js and flask.

I was wondering if there is a way to compile my flask server as an exe.

And run it in the background so the frontend(electron js) can send get and post requests

to my url e.g(get(“http://localhost:5050”)) something like that.

Any help would be appreciated.

Javascript: How to disable button depending on the length of input value

I want to make a simple quiz app in ReactJS. I created carousel cards. When the user answers the question, he will press the next button and a new question will appear.

I would like to prevent pressing the next button before writing any value in the text input.

I tried to achieve this with event.target.value.length property on the onChange event. It works on the first question properly. However, for the next questions, I need to fill in the text input and then remove it to be able to disable the next button.

So, the user cannot pass the first question without filling in the first input. But, he can pass the next questions with empty inputs.

Here is my functional component:

const [submitButton, enableSubmitButton] = useState({
        'isEnabled': false
      });

const handleChange = (event) => {
      if (event.target.value.length > 0) {
          enableSubmitButton({ 'isEnabled': true });
      } else {
          enableSubmitButton({ 'isEnabled': false });
      }
}

return (
     <React.Fragment>
           {posts.map((post) => (
                  <CardLayout key={ post.id } content={
                       <FormGroup row>
                            <Label for={ 'question_' + post.id }>
                                       { post.question + ' ='}
                            </Label>
                            <Input id={'question_' + post.id} 
                                   name={ 'question_' + post.id } 
                                   onChange={ handleChange } />
                       </FormGroup>
                  } />
            ))}
     </React.Fragment>
)

How can I achieve to disable the next button depending on the length of all of the inputs values?

How to re-render component when get the useQuery response

I have onload api call from where I get the isMenuCreated flag and according to its value true or false, I have to display the data. but I’m getting the expected value when I refresh the page. As I can see is when I get the data and state changes but it’s not re-rendering the component.

What I have to do is when I get the data from API, I have to update the value of isMenuCreated to true or false. But I can set it on refresh.

import React, { useEffect, useState } from 'react';
import CategoryFields from './dynamic field component/CategoryFields';

import {
  Button,
  Form,
  Space,
  Select,
  message,
  Collapse,
  Empty,
  Input,
} from 'antd';

import { useLazyQuery, useMutation, useQuery } from '@apollo/client';
import {
  GET_CATEGORY_ITEMS,
  GET_CUISINES_CATEGORY,
  GET_ITEM_VARIANTS,
  GET_MENU_CUISINE,
  GET_RES_BY_OWNER,
  GET_VARIANT_OPTIONS,
} from '../../graphQL/Queries';
import { CREATE_RESTAURANT_MENU } from '../../graphQL/Mutation';

import { getUserId } from '../../utils/auth';
import './menuManagement.css';
import { EditFilled } from '@ant-design/icons';

const { Panel } = Collapse;

const MenuManagement = () => {
  const [isCategoryEdited, setIsCategoryEdited] = useState(false);
  const [isItemEdited, setIsItemEdited] = useState(false);
  const [isMenuCreated, setIsMenuCreated] = useState(false);
  const [restaurantId, setRestaurantId] = useState('');
  const [restaurantName, setRestaurantName] = useState('');
  const [userId, setUserId] = useState();
  const [isLoading, setIsLoading] = useState(true);
  // const [restaurantOwnerData, setRestaurantOwnerData] = useState({});
  // const [defaultActivateKey, setDefaultActivateKey] = useState(null);

  const {
    data: restaurantOwnerData,
    refetch,
    loading,
  } = useQuery(GET_RES_BY_OWNER, {
    variables: {
      ownerId: +userId,
    },
  });

  const [getMenuCuisines, { data: cuisineData }] =
    useLazyQuery(GET_MENU_CUISINE);

  /******* API TO GET RESTAURANT OF LOGGED IN USER *********** */

  // useEffect(() => {
  //   (async () => {
  //     const USER_ID = await getUserId();
  //     setUserId(USER_ID);
  //   })();
  // }, []);

  useEffect(() => {
    console.log('RESDATA', restaurantOwnerData);
  }, [restaurantOwnerData]);

  useEffect(() => {
    console.log('OUTSIDE', loading);
    if (loading === false) {
      console.log('LOADING-->', loading);
    }
  }, [loading]);

  useEffect(() => {
    loading === false &&
      restaurantOwnerData &&
      // (async () => {
      // try {
      console.log(
        'restaurantOwnerData?.getRestaurantByOwner?.isMenuCreated: ',
        restaurantOwnerData?.getRestaurantByOwner?.isMenuCreated
      );
    if (restaurantOwnerData?.getRestaurantByOwner?.isMenuCreated) {
      console.log('set true');
      setIsMenuCreated(true);
    }
    setRestaurantId(restaurantOwnerData?.getRestaurantByOwner?.id);
    setRestaurantName(restaurantOwnerData?.getRestaurantByOwner?.name);

    setIsLoading(false);
    // })();
  }, [loading, restaurantOwnerData]);

  // useEffect(() => {
  //   setRestaurantId(restaurantOwnerData?.getRestaurantByOwner?.id);
  // }, [restaurantOwnerData, restaurantId]);

  useEffect(() => {
    if (restaurantId) {
      getMenuCuisines({ variables: { restaurantId: restaurantId } });
    }
    setUserId(getUserId());
  }, [restaurantId]);

  // useEffect(() => {
  //   setRestaurantName(restaurantOwnerData?.getRestaurantByOwner?.name);
  // }, [restaurantOwnerData]);

  /********** API TO CREATE RESTAURANT MENU ********* */
  const [createRestaurantMenu] = useMutation(CREATE_RESTAURANT_MENU);

  const createRestaurantMenuHandler = async (menuData) => {
    try {
      await createRestaurantMenu({
        variables: {
          restaurantId: restaurantId,
          data: menuData.data,
        },
      }).then((response) => {
        message.success(response?.data?.createRestaurantMenu?.message);
        setIsMenuCreated(true);
        refetch();
      });
    } catch (err) {
      message.error(err);
    }
    refetch();
  };

  const CuisineIdHandler = () => {
    const [getCuisineCategories, { data: cuisineCategory }] = useLazyQuery(
      GET_CUISINES_CATEGORY
    );

    const idHandler = (value) => {
      getCuisineCategories({
        variables: { restaurantId: +restaurantId, cuisineId: +value },
      });
    };

    const [getCategoryItems, { data: itemsData }] =
      useLazyQuery(GET_CATEGORY_ITEMS);

    const [getItemVariants, { data: variantData }] =
      useLazyQuery(GET_ITEM_VARIANTS);

    const [getVariantOptions, { data: optionsData }] =
      useLazyQuery(GET_VARIANT_OPTIONS);

    const categoryIdHandler = (values) => {
      if (values) {
        getCategoryItems({ variables: { categoryId: +values } });
      }
    };

    const variantsIdHandler = (values) => {
      if (values) {
        getItemVariants({ variables: { itemId: +values } });
      }
    };

    const optionIdHandler = (values) => {
      if (values) {
        getVariantOptions({ variables: { itemId: +values } });
      }
    };

    return (
      <Collapse accordion onChange={idHandler}>
        {cuisineData?.getMenuCuisines?.cuisines?.map((cuisine) => {
          return (
            <Panel header={cuisine?.cuisineName} key={cuisine?.id}>
              {cuisineCategory?.getCuisineCategories?.categories?.length > 0 ? (
                <div className='edit-menu'>
                  <h1>CATEGORIES</h1>
                  <Button
                    onClick={() => {
                      setIsCategoryEdited(true);
                      // setDefaultActivateKey(cuisine?.id);
                    }}
                    className='edit-btn'
                    type='primary'
                    icon={<EditFilled />}
                  />
                </div>
              ) : (
                <Empty />
              )}

              <Collapse accordion onChange={categoryIdHandler}>
                {isCategoryEdited &&
                  cuisineCategory?.getCuisineCategories?.categories?.length >
                    0 && (
                    <Form>
                      <Form.Item className='itemPanel' label='Category Name'>
                        <Input />
                      </Form.Item>
                      <Form.Item className='itemPanel'>
                        <Button
                          type='primary'
                          htmlType='submit'
                          onClick={() => setIsCategoryEdited(false)}
                        >
                          Done
                        </Button>
                      </Form.Item>
                    </Form>
                  )}

                {!isCategoryEdited &&
                  cuisineCategory?.getCuisineCategories?.categories?.map(
                    (category) => {
                      return (
                        <Panel header={category.categoryName} key={category.id}>
                          <div className='edit-menu'>
                            <h1>ITEMS</h1>
                            <Button
                              onClick={(e) => {
                                e.stopPropagation();
                                setIsItemEdited(true);
                              }}
                              className='edit-btn'
                              type='primary'
                              icon={<EditFilled />}
                            />
                          </div>

                          <Collapse accordion onChange={variantsIdHandler}>
                            {isItemEdited && (
                              <Form>
                                <Form.Item
                                  className='itemPanel'
                                  label='Description'
                                >
                                  <Input />
                                </Form.Item>
                                <Form.Item className='itemPanel' label='Price'>
                                  <Input />
                                </Form.Item>
                                <Form.Item className='itemPanel'>
                                  <Button
                                    type='primary'
                                    onClick={(e) => {
                                      e.stopPropagation();
                                      setIsItemEdited(false);
                                    }}
                                  >
                                    Done
                                  </Button>
                                </Form.Item>
                              </Form>
                            )}

                            {!isItemEdited &&
                              itemsData?.getCategoryItems?.items?.map(
                                (item) => (
                                  <React.Fragment key={item.id}>
                                    <h3 className='itemPanel'>
                                      Description: {item.itemDescription}
                                    </h3>
                                    <h3 className='itemPanel'>
                                      Price: {item.itemPrize}
                                    </h3>
                                    <Panel header={item.itemName} key={item.id}>
                                      <div className='edit-menu'>
                                        <h1>VARIANTS</h1>
                                        <Button
                                          className='edit-btn'
                                          type='primary'
                                          icon={<EditFilled />}
                                        />
                                      </div>

                                      <Collapse onChange={optionIdHandler}>
                                        {variantData?.getItemVariants?.variants?.map(
                                          (variant) => (
                                            <React.Fragment key={variant.id}>
                                              <h3 className='itemPanel'>
                                                Description:{' '}
                                                {variant.description}
                                              </h3>
                                              <h3 className='itemPanel'>
                                                Multiple:{' '}
                                                {variant.isMulti === 'true'
                                                  ? 'Yes'
                                                  : 'No'}
                                              </h3>
                                              <h3 className='itemPanel'>
                                                Required:{' '}
                                                {variant.isRequired === 'true'
                                                  ? 'Yes'
                                                  : 'No'}
                                              </h3>
                                              <Panel
                                                header={variant.variantName}
                                                key={variant.id}
                                              >
                                                <div className='edit-menu'>
                                                  <h1>OPTIONS</h1>
                                                  <Button
                                                    className='edit-btn'
                                                    type='primary'
                                                    icon={<EditFilled />}
                                                  />
                                                </div>

                                                <Collapse>
                                                  {optionsData?.getVariantOptions?.options?.map(
                                                    (option) => (
                                                      <React.Fragment
                                                        key={option.id}
                                                      >
                                                        <h3 className='itemPanel'>
                                                          Price: {option.prize}
                                                        </h3>
                                                        <Panel
                                                          header={option.name}
                                                          showArrow={false}
                                                        ></Panel>
                                                      </React.Fragment>
                                                    )
                                                  )}
                                                </Collapse>
                                              </Panel>
                                            </React.Fragment>
                                          )
                                        )}
                                      </Collapse>
                                    </Panel>
                                  </React.Fragment>
                                )
                              )}
                          </Collapse>
                        </Panel>
                      );
                    }
                  )}
              </Collapse>
            </Panel>
          );
        })}
      </Collapse>
    );
  };

  return loading ? (
    <div style={{ width: '100%' }}>Loading</div>
  ) : (
    <div className='main'>
      {isMenuCreated ? (
        <>
          <h1>{restaurantName}'s Menu</h1>
          <CuisineIdHandler />
        </>
      ) : (
        // restaurantOwnerData &&
        <Form
          initialValues={{ isRequired: false, isMulti: false }}
          onFinish={(data) => createRestaurantMenuHandler(data)}
          layout={{
            labelCol: { span: 4 },
            wrapperCol: { span: 14 },
          }}
        >
          <>
            <Form.List name='data'>
              {(data, { add, remove }) => {
                return (
                  <div>
                    {data.map((field) => (
                      <Space
                        className='cuisine-category-main'
                        key={field.key}
                        align='start'
                      >
                        <Form.Item
                          {...field}
                          name={[field.name, 'cuisine']}
                          key={field.key}
                          rules={[
                            {
                              required: true,
                              message: 'Please select any cuisine',
                            },
                          ]}
                        >
                          <Select
                            showSearch
                            style={{ width: 200 }}
                            placeholder='Cuisine Name'
                          >
                            {cuisineData?.getMenuCuisines?.cuisines?.map(
                              (cuisine) => {
                                return (
                                  <Select.Option
                                    name='cuisine'
                                    key={cuisine.id}
                                    value={cuisine.id}
                                  >
                                    {cuisine.cuisineName}
                                  </Select.Option>
                                );
                              }
                            )}
                          </Select>
                        </Form.Item>

                        <Form.Item>
                          <CategoryFields fieldKey={field.name} />
                        </Form.Item>

                        <Button
                          className='remove-btn'
                          type='primary'
                          onClick={() => {
                            remove(field.name);
                          }}
                        >
                          Remove Cuisines
                        </Button>
                      </Space>
                    ))}

                    <Button
                      style={{ marginBottom: '10px' }}
                      type='primary'
                      onClick={() => {
                        add();
                      }}
                      block
                    >
                      Add Cuisine
                    </Button>
                  </div>
                );
              }}
            </Form.List>
          </>

          <>
            <Button block className='add-cuisine-btn' htmlType='submit'>
              Submit
            </Button>
          </>
        </Form>
      )}
    </div>
  );
};

export default MenuManagement;

AWS SDK JavaScript v3 / How to use ExpressionAttributeNames within dynamoDB Scan Command?

I’m using a Lambda function which gets me the user email from a user id within a dynamoDB table. I use the dynamoDB scan command to scan over all items within the dynamoDB table. I use the new v3 AWS JS SDK.

Question: Why does ExpressionAttributeNames not work properly in my case?

This works:

const params = {    
  FilterExpression: "user_info.user_id = :userid",  
  ExpressionAttributeValues: {
   ":userid": { S: user_id }
  },
  ProjectionExpression: "user_email",
  TableName: aws_table,
}

But this does NOT work, why?

const params = {
  FilterExpression: "#xyz = :userid",  
  ExpressionAttributeNames: {
    "#xyz": "user_info.user_id"  // <- filter does not work like this (returns 0 findings)
  },
  ExpressionAttributeValues: {
    ":userid": { S: user_id }
  },
  ProjectionExpression: "user_email",
  TableName: aws_table,
};

My Lambda scan operation code itself looks like:

const  { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const  { ScanCommand } = require("@aws-sdk/client-dynamodb");
const ddbClient = new DynamoDBClient({ region: aws_region });
...  

const run = async () => {
  try {
    const data = await ddbClient.send(new ScanCommand(params));
    data.Items.forEach(function (element, index, array) {
      console.log(element);
    });
    return data;
  } catch (err) {
    console.log("Error", err);
  }
}

await run();

npm i @aws-sdk/client-dynamodb

Documentation:

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/interfaces/scancommandinput.html#expressionattributenames

state value does not change when using useEffect hook

well I have:

const [bgColor, setBgcolor] = useState(false);

and :

useEffect(() => {
    if (window.location.pathname === '/SignUp') {
        setBgcolor(true);
    } else {
        setBgcolor(false);
    }
    console.log(bgColor)
    return () => {
        setBgcolor(false);
    }
}, [])

What I want to do : is when i reload the page or rerender the page i check the current pathname if it is equal to /Signup I set the bgColor to true but here at every time i reload give me false!

Using Canvas globalCompositeOperation to reduce the amount of red, green or blue in an image

I’m writing an image editor (more as a learning curve for HTML5 Canvas rather than any other reason), and one function I’m getting stuck on is adjusting the RGB colours over the entire image.
Using three range sliders (for Red, Green and Blue), with the range going from -255 to +255, I call a function to add, or remove, the appropriate channels. Adding is easy, seemingly, by using the globalCompositeOperation of “lighter” and drawing a coloured rectangle over the entire image. However, reducing the appropriate channel is seeming to be a tad more difficult.
I had originally just set the colour of the rectangle to be full on with the other two channels, leaving the one we want to reduce as zero (with “lighter” being used). But this just made the image, well, lighter…as expected.
I’ve tried various other variations, with “difference” being the closest. However, go too far and it begins to add that channel to the darker regions.
I’ve also tried “darker”, as I thought it might be the opposite of “lighter”, but that doesn’t produce the desired result.
Maybe the answer is staring me in the face, but I just can’t see it.
The function is below:

function adjustColour()
{
 var value=0;
 var ctx=document.getElementById('theCanvas').getContext('2d');
 ctx.putImageData(originalPicData,0,0);
 for(var colour=0;colour<3;colour++)
 {
  if(colour==0)value=document.getElementById('adjustred').value;
  if(colour==1)value=document.getElementById('adjustgreen').value;
  if(colour==2)value=document.getElementById('adjustblue').value;
  if(value!=0)
  {
   if(value>0)
   {
    ctx.globalCompositeOperation='lighter';
    if(colour==0)ctx.fillStyle='rgb('+Math.abs(value)+',0,0)';
    if(colour==1)ctx.fillStyle='rgb(0,'+Math.abs(value)+',0)';
    if(colour==2)ctx.fillStyle='rgb(0,0,'+Math.abs(value)+')';
   }
   if(value<0)
   {
    ctx.globalCompositeOperation='difference';
    if(colour==0)ctx.fillStyle='rgb('+Math.abs(value)+',0,0)';
    if(colour==1)ctx.fillStyle='rgb(0,'+Math.abs(value)+',0)';
    if(colour==2)ctx.fillStyle='rgb(0,0,'+Math.abs(value)+')';
   }
   ctx.fillRect(0,0,image.width,image.height);
  }
 }
 ctx.globalCompositeOperation='source-over';
 displayImage();
}

Display Image just redraws and rescales the image to fit on the screen from the hidden canvas (“theCanvas”). And originalPicData is the original image grabbed before any manipulation (so we can restore it if Cancel is clicked on, for example).