localStorage results in function being loaded infinite times - javascript

I am filtering what users see based on a selected value. Users can choose between consumer and commercial with the code supposed to be adding "/commercial" to the URL of commercial users and "/consumer" being added to the URL of consumer users.
This is the current code I am using, which results in the below functions being run every time the page is loaded, resulting in an infinite loop.
/* Hides non-commercial products */
function commercial() {
window.location.assign(window.location.origin + "/collections/{{ collection.handle }}/commercial");
localStorage.setItem("saved", "0");
}
/* Hides non-consumer products */
function consumer() {
window.location.assign(window.location.origin + "/collections/{{ collection.handle }}/consumer");
localStorage.setItem("saved", "1");
}
/* Shows all products */
function reset() {
window.location.assign(window.location.origin + "/collections/{{ collection.handle }}");
localStorage.removeItem("saved");
}
UPDATED CODE: LocalStorage results in the above functions being run every time the page is loaded, resulting in an infinite loop. Below is mt LocalStorage get the saved value of the key code:
/* Remember last clicked button and store it in LocalStorage */
window.addEventListener('load', (event) => {
const value = localStorage.getItem("saved");
if (value == "0") {
commercial();
} else if (value == "1") {
consumer()
} else {
reset()
}
});
This is what the code outputs: (DJI is just an example of a collection)
saved == "0" outputs /collections/dji/commercial
saved == "1" outputs /collections/dji/consumer
saved == "2" outputs /collections/dji/

window.location.href holds the complete URL thus it's appending to previous URL, instead use window.location.origin.
window.location.assign(window.location.origin + "/commercial");

Related

Angular - window - EventListener - refresh main tab when other close

I use Angular 7. I deal with documents When I click on "INDEXER" button, a second tab is opened with content of the first document and a form.
When the form is filled, the second document appears on this second tab etc...
When all the documents are ok, this second tab close and and the first main tab must be refreshed.
Code for second tab :
iterate(index) {
this.loadingAction = false;
if (index < this.workflowList.length) {
this.getDetails(this.workflowList[index]);
} else {
localStorage.setItem('actualise', 'true');
window.close();
}
}
Code for the first tab
ngOnInit() {
const that = this;
// COMMUNICATION INTER ONGLETS
window.addEventListener('storage', (event) => {
console.log(event.key)
// console.log('event storage = LocaleStorage : ' + event.storageArea !== localStorage);
if (event.storageArea !== localStorage) {
return;
}
if (event.key === 'actualise') {
// if (event.storageArea.getItem('actualise') === 'true') {
console.log('appel backend')
this.refresh();
} else {
console.log(localStorage.getItem('actualise'));
}
});
}
Problem is that backend is called multiple times and when there are a lot of documents, it's very long.
I updated code in order the backend is called one time, it works but the screen is not refreshed
Original : Backend called multiple time and screen refresh
Update: backend called one time but screen is not refreshed
How can I solve that ?

Prestashop set catalog mode ON/OFF if user unlogged/logged

I'm working on a prestashop module to set the catalog mode ON or OFF if user is unlogged or logged.
Works great but got a problem.
I don't want unlogged users see prices at all and allowed to order. But with the solution I found, when first connection (mode catalog OFF) unlogged user load the page, the catalog mod turn ON, but he can see prices (has to reload to hide prices) So, first load set catalog mode ON and second load display real catalog mode.
I found a js script to reload automatically to take effect with the new mode but obviously, loading time of the page is two times longer.
Here is the function :
public function hookHeader()
{
$logged = $this->context->customer->isLogged();
if (!$logged) {
Configuration::updateValue('PS_CATALOG_MODE', true);
} else {
Configuration::updateValue('PS_CATALOG_MODE', false);
}
// reload the page once more
echo '
<script type="text/javascript">
(function() {
if( window.localStorage ) {
if( !localStorage.getItem( "firstLoad" ) ) {
localStorage[ "firstLoad" ] = true;
window.location.reload();
} else {
localStorage.removeItem( "firstLoad" );
}
}
})();
</script>
';
}
Hope somebody could help me with this. Thank you.
Your solution has a problem.
You're updating the value inside the database: if multiple users are browsing the site, the value will be turned on/off/on/off/..., in other words it's "unstable".
The next customer that visits the site will get the current value (can be on and off).
Instead, you should toggle the value only for that customer. I wrote an override for Configuration class, that check if you're trying to get PS_CATALOG_MODE, then check if you'er logged in and returns 0 or 1. Be careful to cache this value using static variables (so you don't have to check multiple times).
But this solution has a flaw too. It checks the key of the request configuration variable everytime.
A better solution would be to change the value of this during the session. Configuration variables are actually held in a PHP array during the session.
You should change it here:
https://github.com/PrestaShop/PrestaShop/blob/1.6.1.x/classes/Configuration.php#L203
possibly by overridding
https://github.com/PrestaShop/PrestaShop/blob/1.6.1.x/classes/Configuration.php#L140
This is what I had in mind by overriding loadConfiguration:
<?php
// placed in /override/classes/Configuration.php
class Configuration extends ConfigurationCore
{
public static function loadConfiguration()
{
parent::loadConfiguration();
// 'global' because I assume you're not runing multishop
self::$_cache[self::$definition['table']][0]['global']['PS_CATALOG_MODE'] = !Context::getContext()->customer->isLogged();
}
}
I wrote this from memeroy so be sure to check the anmes, etc. I assume you're running > PS1.6
Why don't you just use the group settings? Customer group settings allow you to set the "show prices" option to "false" for visitors, and "true" for customers, for example.
The solution we find with gskema is to override the get() function of the Configuration class :
<?php
// placed in /override/classes/Configuration.php
class Configuration extends ConfigurationCore
{
public static function get($key, $id_lang = null, $id_shop_group = null, $id_shop = null)
{
if (defined('_PS_DO_NOT_LOAD_CONFIGURATION_') && _PS_DO_NOT_LOAD_CONFIGURATION_) {
return false;
}
// If conf if not initialized, try manual query
if (!isset(self::$_cache[self::$definition['table']])) {
Configuration::loadConfiguration();
if (!self::$_cache[self::$definition['table']]) {
return Db::getInstance()->getValue('SELECT `value` FROM `'._DB_PREFIX_.bqSQL(self::$definition['table']).'` WHERE `name` = "'.pSQL($key).'"');
}
}
$id_lang = (int)$id_lang;
if ($id_shop === null || !Shop::isFeatureActive()) {
$id_shop = Shop::getContextShopID(true);
}
if ($id_shop_group === null || !Shop::isFeatureActive()) {
$id_shop_group = Shop::getContextShopGroupID(true);
}
if (!isset(self::$_cache[self::$definition['table']][$id_lang])) {
$id_lang = 0;
}
if ($id_shop && Configuration::hasKey($key, $id_lang, null, $id_shop)) {
if($key == 'PS_CATALOG_MODE' && Context::getContext()->controller->controller_type == 'front')
{
return !Context::getContext()->customer->isLogged() || self::$_cache[self::$definition['table']][$id_lang]['shop'][$id_shop][$key];
} else {
return self::$_cache[self::$definition['table']][$id_lang]['shop'][$id_shop][$key];
}
} elseif ($id_shop_group && Configuration::hasKey($key, $id_lang, $id_shop_group)) {
if($key == 'PS_CATALOG_MODE' && Context::getContext()->controller->controller_type == 'front')
{
return !Context::getContext()->customer->isLogged() || self::$_cache[self::$definition['table']][$id_lang]['group'][$id_shop_group][$key];
} else {
return self::$_cache[self::$definition['table']][$id_lang]['group'][$id_shop_group][$key];
}
} elseif (Configuration::hasKey($key, $id_lang)) {
if($key == 'PS_CATALOG_MODE' && Context::getContext()->controller->controller_type == 'front')
{
return !Context::getContext()->customer->isLogged() || self::$_cache[self::$definition['table']][$id_lang]['global'][$key];
} else {
return self::$_cache[self::$definition['table']][$id_lang]['global'][$key];
}
}
return false;
}
}
/!\ still comparing the key value every time someone tries to get a config variable, which may slow down the shop just slightly.
EDIT
Add a condition if Context is front office to fixe back office issue 'Call isLogged on NULL'

Show hidden div in page after clicking link to open the page

I have an HTML page in which a hidden div becomes visible when a button is clicked. Something like this:
$('#display').click(function(){
$('#itemList').removeClass('hide');
...
})
On another page, there is a link which when clicked takes the user back to the earlier page, and the element with id='itemList' on that page has to become visible. The code is something like this:
<a href='firstHTML.php'> View items</a>
I am not sure what else to add to the code to make the other page appear with the previously hidden element visible. Can somebody help please?
One of the most probable solution is localStorage .Where as you may also implement Cookies or string query to pass value to other page.
I am showing the use of localstorage , you may store the id in localStorage on click of anchor as below
<a href='firstHTML.php' data-showId='itemList'> View items</a>
Now bind event on anchor
$("[data-showId]").bind('click',function(){
var idToShow=$(this).attr('data-showId');
if(idToShow)
store('visibleId', idToShow);
});
Now all you need to define these functions .
function setup() {
var tmp = get('visibleId');
if (tmp)
showDiv(tmp);
}
function showDiv(cls) {
$("#"+cls).removeClass('hide');
}
function get(name) {
if (typeof (Storage) !== "undefined") {
return localStorage.getItem(name);
} else {
window.alert('Please use a modern browser to properly view this template!');
}
}
function store(name, val) {
if (typeof (Storage) !== "undefined") {
localStorage.setItem(name, val);
} else {
window.alert('Please use a modern browser to properly view this template!');
}
}
Now call setup() on dom ready..
First of all, I would use the jQuery function to hide/show the List instead of using an own CSS class for it:
$('#display').click(function(){
$('#itemList').show();
...
})
Then a possible approach for your problem could be to use a get Parameter for this, for example:
<a href='firstHTML.php?list=show'> View items</a>
And with jQuery
Create a helperfunction (Taken from Get url parameter jquery Or How to Get Query String Values In js):
$.urlParam = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results==null){
return null;
}else{
return results[1] || 0;
}
}
Read out the property:
var listStatus = $.urlParam('list');
Unhide the list in case it should be shown:
$( document ).ready(function() {
if(listStatus == 'show') {
$('#itemList').show();
}
});

Persist a page action's state in a chrome extension

So, I have the following code:
var clicks = 0; // click counter
// Make sure this only runs on facebook
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (tab.url.indexOf("facebook.com") > -1) {
chrome.pageAction.show(tabId);
}
});
// Called when the user clicks on the page action.
chrome.pageAction.onClicked.addListener(function(tab) {
if (clicks == 0) {
chrome.pageAction.setIcon({path: "dontlike.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "idontlike", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Hide like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display="none"; }'
});
}
else {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display=""; }'
});
}
// wrap coutner around
clicks++;
if (clicks > 1)
clicks = 0;
});
for a chrome extension that hides all "Like" buttons on facebook when a pageaction icon is clicked. This works; however, any time a new facebook url is loaded, the state of the extension is lost, e.g. if the button is in dislike mode (hide all likes), if I go to a new page, it is reset to like mode.
I had an idea to persist the state of the extension using the click counter, and to make the code more functional with something like the following
var clicks = 0; // click counter
function like() {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display="none"; }'
});
clicks++;
if (clicks > 1) {
clicks = 0;
}
}
function dislike() {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display=""; }'
});
clicks++;
if (clicks > 1) {
clicks = 0;
}
}
// Make sure this only runs on facebook
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (tab.url.indexOf("facebook.com") > -1) {
chrome.pageAction.show(tabId);
if (clicks == 0) {
like();
}
else {
dislike();
}
}
});
// Called when the user clicks on the page action.
chrome.pageAction.onClicked.addListener(function(tab) {
if (clicks == 0) {
like();
}
else {
dislike();
}
});
But that code doesn't work at all (when I click on the page action icon, nothing happens and no error messages appear in the chrome console).
I'm new to JS and Chrome Extensions. Is there an easy way to persist the state of my extension, and a better way to execute the script I need to hide all like buttons?
Thank you!
The question of states in chrome extension can have several answers. The choice depend of the situation. Whet I have understand in your case is that you only have tow states, so I will give you some idea.
1. Persistent background script
By default, background script is loaded at chrome startup, so it lives during the whole execution of chrome, until the user explicitly close chrome. In combination with Content Script, you can have a state full system.
So You can use this background script to save a state during the execution and inform listening content scripts of the changes :
background.js
var state = 0;
chrome.pageAction.onClicked.addListener(function(tab) {
if (state = 0) {
state = 1;
state0Actions(); //Do what you want
}
else {
state = 0;
state1Actions(); //Do what you want
}
//Inform content scripts that the state have changed
chrome.tabs.sendMessage(tab.id, {state : state});
});
//At initialisation, Content scipts will request the current state to background script.
chrome.runtime.onMessage(function(message, sender, callback){
if(message.getState) callback({state : state});
});
You can then inject a content script to all facebook pages by adding this to your manifest.json file
"content_scripts" :
[
{
"matches": ["https://www.facebook.com/*","http://www.facebook.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
]
It will automatically inject the contentScipt.js script to all page beginning with http(s)://www.facebook.com.
contenScript.js
//the actions to do for each states
function state0Actions()
{
//Do what you want for the state 0
}
function state1Actions()
{
//Do what you want for the state 1
}
//Message will be received at each update of state in the background page
chrome.runtime.onMessage.addListner(function(message, sender, callback))
{
//Check the message is valid
if(message.state == null)
{
console.log("Unreconized message");
return;
}
//Do actions for the right state
//You also can use if statements here... Switch are more used when there is lots of states
switch(message.state) {
case 0 : state0Actions(); break;
case 1 : state1Actions(); break;
}
}
//Request the current state to initialise the script
chrome.runtime.sendMessage({getState: true});
Here, the onMessage handler will be call a first time when he is loaded and then each time the background change the state.
Pay attention that the state will be reset at the chrome startup.
2. Chrome storage
You can use chrome.storage API to manage the state. The main point of this is that the state will be saved and will not be reset at chrome startup.
To do this, you have pretty the same background code :
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.storage.local.get("state", function(result)
{
//First initialisation of the state in the local storage
if(result.state == null)
{
chrome.storage.local.set({state: 0});
state0Actions(); //Do what you want
}
else if (result.state == 0) {
result.state = 1;
state0Actions(); //Do what you want
}
else {
result.state = 0;
state1Actions(); //Do what you want
}
//Save the new state to the storage
chrome.storage.set({state: result.state});
}
});
And the content script will listen changes of the local storage instead of wating update notification from the background page :
//the actions to do for each states
function state0Actions()
{
//Do what you want for the state 0
}
function state1Actions()
{
//Do what you want for the state 1
}
chrome.storage.local.onChanged.addListener(function(changes, areaName)
{
if(areaName != "local" || changes.state == null) return;
switch(changes.state)
{
case 0 : state0Actions(); break;
case 1 : state1Actions(); break;
}
})
chrome.storage.local.get("state", function(result){
if(result.state == null) state0Actions(); //Do what you want if the state is not yet initialised
else if (result.state == 0) state0Actions(); //Do what you want
else if (result.state == 1) state1Actions(); //Do what you want
})
You also can use chrome.storage.sync instead of chrome.storage.local for a shared state with all user's devices.
This are to way to play with state. you have to compare what is the better for your use case. The code I have written is not tested, they are only example to illustrate my explanation.
Don't forget to check Chrome API documentation
When the extension is using a non-persistent background page aka Event page it is unloaded after ~5 seconds of inactivity. And every time it's reloaded the code runs again and all variables are re-initialized, thus losing the previous state.
The solution is to store the state in localStorage which doesn't require any additional permissions in manifest.json:
Initialization:
var clicks = localStorage.clicks || 0; // click counter
Toggling and storing (no need for ++ and if):
var clicks = localStorage.clicks = 1 - clicks;
The value will be stringified and stored as "0" or "1" but for the above arithmetic it's not a problem.

Getting textbox value on the same page but different php file

I have this main page that loads another php file on onchange event of the dropdown. I use this to load the page:
function get_value(){
if($("#dropdwn").val()=="0"){
//load nothing
}else{
$('#txtval').val($("#dropdown").val());
$('#load_page').html('<p align="center"><br/><img src="images/popuploader.gif" /><br/><br/></p>');
$('#load_page').load('load_xml.php');
}
}
For now I put the value of dropdown on the textbox but will also try to get the value of dropdown.
The problem is on the second php file that loads on the main page. I can't get the value of $txtval=$_POST['txtval'] when I use this. I will need the value for if else condition.
First you need to sent the parameter to the resource load_xml.php.
function get_value() {
if ($("#dropdwn").val() == "0") {
//load nothing
} else {
var val = $("#dropdown").val();
$('#txtval').val(val);
$('#load_page').html('<p align="center"><br/><img src="images/popuploader.gif" /><br/><br/></p>');
$('#load_page').load('load_xml.php?txtval=' + val);
}
}
The the load method uses a GET request, not a POST method.
$txtval=$_GET['txtval']
If you want to sent a POST method, then use the syntax
function get_value() {
if ($("#dropdwn").val() == "0") {
//load nothing
} else {
var val = $("#dropdown").val();
$('#txtval').val(val);
$('#load_page').html('<p align="center"><br/><img src="images/popuploader.gif" /><br/><br/></p>');
$('#load_page').load('load_xml.php?', {
txtval: val
});
}
}
then
$txtval=$_POST['txtval']

Categories