webserver (esp8266) with long loop duration - webserver

I'm currently building a webserver with webpage to control led strips. The website runs fine and I can control a single LED perfectly. When I select a rainbow animation for example (cycle through evry color of the rainbow) this loop takes quit a lot of time (5 seconds per color). When the node mcu (ESP8266) is in that loop inputs from the webserver are not seen. I don't want to write in every animation that I wrote to check for the inputs (these are 50 animations or more) so I came up with an interrupt. I start an timer and everytime (executed in less then 250ms to trigger watchdog) the interrupt is triggered the handlewebserver is executed. When I put my handleClient in the void loop everything works fine, but when I put it in my timerInterrupt the page start constantly reloading. Can anyone tell why? ps: with only controlling a simple led on or off this doenst work eather, thats why I dont incuded the extra code here, you can include a very long delay after turning D4 high to test this :)
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Ticker.h>
Ticker blinker;
const char ssid[] = "*****";
const char pass[] = "*****";
// Web server port
const uint16_t port = 80;
boolean ledStatus = false;
// Web server object declaration
ESP8266WebServer server(port);
/*
String webPage =
"<html>\r\n" \
"<head>\r\n" \
"<title>ESP8266 Basic Page LED</title>\r\n" \
"</head>\r\n" \
"<body>\r\n" \
"<h1>LED</h1>\r\n" \
"<form method=\"post\">\r\n" \
"<input type=\"submit\" name=\"led\" value=\"ON\">\r\n" \
"<input type=\"submit\" name=\"led\" value=\"OFF\">\r\n" \
"</form>\r\n" \
"</body>\r\n" \
"</html>\r\n";
*/
const char MAIN_page[] PROGMEM = R"=====(
<HTML>
<HEAD>
<title>ESP8266 Basic Page LED</title>
</HEAD>
<style>
.panel{
width: 100%;
height: 40vh;
margin-bottom: 10px;
background-color rgba(255,208,142,1);
}
.slidecontainer {
width: 100%;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 15px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {
opacity: 1;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
background: #89cff0;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.color-picker {
width: 200px;
height: 100px;
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#simonwep/pickr/dist/themes/classic.min.css"/> <!-- 'classic' theme -->
<BODY>
<div class="color-picker"></div>
<div class="panel" id="panel"></div>
<h1>LED</h1>
<form method="post" id="formie">
<input type="submit" name="led" value="ON">
<input type="submit" name="led" value="OFF">
leds dimmen:
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" name="slider" class="slider" id="myRange">
<p class="valueSlider">Value: <span id="demo"></span></p>
</div>
<input type="text" id="field" name="fname" value="John"><br><br>
</form>
<script src="https://cdn.jsdelivr.net/npm/#simonwep/pickr/dist/pickr.min.js"></script>
<script>
let panel = document.getElementById('panel');
// Simple example, see optional options for more configuration.
const pickr = Pickr.create({
el: '.color-picker',
theme: 'classic', // or 'monolith', or 'nano'
swatches: [
'rgba(244, 67, 54, 1)',
'rgba(233, 30, 99, 1)',
'rgba(156, 39, 176, 1)',
'rgba(103, 58, 183, 1)',
'rgba(63, 81, 181, 1)',
'rgba(33, 150, 243, 1)',
'rgba(3, 169, 244, 1)',
'rgba(0, 188, 212, 1)',
'rgba(0, 150, 136, 1)',
'rgba(76, 175, 80, 1)',
'rgba(139, 195, 74, 1)',
'rgba(205, 220, 57, 1)',
'rgba(255, 235, 59, 1)',
'rgba(255, 193, 7, 1)',
'rgba(255, 193, 7, 1)',
'rgba(255, 193, 7, 1)',
'rgba(255, 193, 7, 1)'
],
components: {
// Main components
preview: true,
opacity: false,
hue: true,
// Input / output Options
interaction: {
hex: false,
rgba: false,
hsla: false,
hsva: false,
cmyk: false,
input: false,
clear: false,
save: false
}
}
});
pickr.on('change', (color, instance) => {
//console.log('change', color, instance);
var testieee = color.toRGBA(); // Returns [r, g, b, a]
console.log(testieee);
this.panel.style.backgroundColor = 'rgba(${color[0]},${color[1]},${color[2]},${color[3]})';
var myForm = document.getElementById('formie');
var hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'myarray';
hiddenInput.value = JSON.stringify(testieee);
var testelement = document.getElementById('field');
testelement.value = JSON.stringify(testieee);
})
</script>
</BODY>
</HTML>
)=====";
void handleRoot()
{
String s = MAIN_page; //Read HTML contents
server.send(200, "text/html", s); //Send web page
}
void handleLed()
{
// *** Turn on or off the LED ***
if (server.arg("led") == "ON")
digitalWrite(D4, LOW);
else if (server.arg("led") == "OFF")
digitalWrite(D4, HIGH);
if (server.arg("slider").toInt() > 50)
digitalWrite(D2, HIGH);
else
digitalWrite(D2, LOW);
server.sendHeader("Location","/");
server.send(303);
}
//=======================================================================
void ICACHE_RAM_ATTR onTimerISR(){
//digitalWrite(LED,!(digitalRead(LED))); //Toggle LED Pin
server.handleClient(); /* when It moves to here the server is constantly reloading and starst again a few times! */
timer1_write(250000);
Serial.printf("execute interrupt");
}
void setup()
{
// Set pin as output
pinMode(D4, OUTPUT);
pinMode(D2, OUTPUT);
// Setup serial communication
Serial.begin(74880);
// *** Connect to a WiFi acces point ***
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.printf(" Connected\n");
Serial.printf("IP address: %s\n", WiFi.localIP().toString().c_str());
// *** Start web server ***
server.on("/", HTTP_GET, handleRoot);
server.on("/", HTTP_POST, handleLed);
server.begin();
Serial.printf("Web server started at port %d\n", port);
//Initialize Ticker every 0.5s / 2 and a little bit less --> so under 250ms
timer1_attachInterrupt(onTimerISR);
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);
timer1_write(250000); //120000 us
}
void loop()
{
//server.handleClient(); /* when It stays here everything is fine! */
//server.handleClient();
//Serial.print(server.arg("fname"));
}

Related

Is there any shortcut to change 1 word per line to any #words per line?

For example:
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
to
one two three
four five six
seven eight nine
ten eleven twelve
I couldn't figure out how to do this and was only able to do vice versa on vscode.
Not a shortcut, but you can easily do such job with find ans replace.
Ctrl+H
Find what: (\w+)\R(\w+)\R(\w+)
Replace with: $1 $2 $3
CHECK Wrap around
CHECK Regular expression
Replace all
Explanation:
(\w+) # group 1, 1 or more word characters
\R # any kind of linebreak
(\w+) # group 2, 1 or more word characters
\R
\R # any kind of linebreak
Screenshot (before):
Screenshot (after):
Not VS Code, but you can do that using this snippet:
Just bookmark this answer if you need it again (or copy and paste the snippet info into an HTML file on your device). Also, I think the "Copy to clipboard" button doesn't work because the snippet runs in a cross-origin iframe, but it should work in a same-origin context.
function splitWordsPerLine (text, wpl = 1) {
let result = '';
wpl = wpl < 1 ? 1 : wpl;
let count = wpl;
for (const word of text.split(/\s+/)) {
count -= 1;
let line = word;
if (count === 0) {
line += '\n';
count = wpl;
}
else line += ' ';
result += line;
}
return result.trim();
}
function getWPL (numberInput) {
if (!numberInput) return 1;
const wpl = parseInt(numberInput.value, 10);
return Number.isNaN(wpl) ? 1 : wpl;
}
function handleInput (event) {
const wpl = getWPL(event.target);
const textInput = document.getElementById('text');
if (!textInput) return;
textInput.value = splitWordsPerLine(textInput.value, wpl);
}
async function handleClick (event) {
let message = 'Copying failed 😭';
const textInput = document.getElementById('text');
try {
if (!textInput) throw new Error('No input found');
await navigator.clipboard.writeText(textInput.value);
message = 'Text copied ✅';
}
catch {}
textInput?.select();
const setText = str => event.target.textContent = str;
setText(message);
setTimeout(() => setText('Copy to clipboard'), 1500);
}
function handlePaste (event) {
const text = event.clipboardData?.getData('text');
if (!text) return;
const wpl = getWPL(document.getElementById('wpl'));
event.target.value = splitWordsPerLine(text, wpl);
event.preventDefault();
}
document.getElementById('wpl')?.addEventListener('input', handleInput);
document.getElementById('copy')?.addEventListener('click', handleClick);
document.getElementById('text')?.addEventListener('paste', handlePaste);
html {
box-sizing: border-box;
height: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
font-family: sans-serif;
height: 100%;
margin: 0;
padding: 1rem;
}
.container {
display: flex;
gap: 0.5rem;
}
.container.vertical {
flex-direction: column;
height: 100%;
}
#copy {
background-color: black;
border: 0;
color: white;
display: inline-flex;
align-items: center;
font-size: 1rem;
padding: 0.5rem;
}
#wpl, #text {
border: 1px solid;
font-family: monospace;
padding: 0.5rem;
}
#wpl {
font-size: 1.5rem;
width: 5rem;
}
#text {
font-size: 1rem;
height: 100%;
width: 100%;
resize: none;
white-space: pre;
}
<div class="container vertical">
<div class="container">
<input id="wpl" type="number" min="1" step="1" value="1" />
<button id="copy">Copy to clipboard</button>
</div>
<textarea id="text" rows="0" cols="0" placeholder="Select number of words per line, then paste your text here"></textarea>
</div>
You can use extension Select By and the command selectby.lineNr
Place the cursor on the first line
execute command: Place cursor based on line number, uses boolean expression
enter expression: c+3k to place a cursor every 3 lines
maybe expression: c+3k && n<50 to limit the end line to use
now use End Space Delete as often as needed
press Esc to exit Multi Cursor Mode

ionic 5 forcing light theme for the browser app

i have built this app https://cvirus.app and build and deployed on browser.
on chrome browser on android phones it renders dark background (as dark theme is being applied by default it seems).
I want to force light theme everywhere no matter what device what browser. how do I do that?
commenting dark colors portins in theme/variables.scss did not work
To resolve this, you have to go to theme/variable.scss at the src folder and locate
#media (prefers-color-scheme: dark)
And change from dark to light. If you have not modified the variable.scss. it should be in line 79. Click to see the screenshot of my own code I hope this helps.
Since version 79 of the Chrome browser, you can choose between CSS prefers-color-sheme media feature values. Ionic respects this media feature and reacts to changes to it.
To change it without changing your OS preference and color theme, you need to open the Rendering panel in the Chrome Dev Tools:
Open the the Chrome Dev Tools.
Press Crtl+Shift+P on PC or Cmd+Shift+P on Mac.
Type "Show rendering" in the Command Palette.
Press Enter with the option Drawer | Show rendering selected, which should be already selected.
The Rendering drawer opens. Then on the "Emulate CSS media..." option, select the color scheme you prefer.
you should comment ios body and md body classes at the theme/variables.scss then it will become light mode.
In ionic 5+ Just rename prefers-color-scheme: dark to prefers-color-scheme: light Then It will not switch to dark theme no matter what.
In my case, commenting the dark theme in theme/variables.scss works well.
I comment on line 70 #media (prefers-color-scheme: dark) {
Until the end of the file.
Here is my file
// Ionic Variables and Theming. For more info, please see:
// http://ionicframework.com/docs/theming/
/** Ionic CSS Variables **/
:root {
--ion-color-primary : #633A82;
--ion-color-primary-rgb : 99, 58, 130;
--ion-color-primary-contrast : #FFFFFF;
--ion-color-primary-contrast-rgb : 255, 255, 255;
--ion-color-primary-shade : #573372;
--ion-color-primary-tint : #734E8F;
--ion-color-secondary : #72B5B7;
--ion-color-secondary-rgb : 114, 181, 183;
--ion-color-secondary-contrast : #000000;
--ion-color-secondary-contrast-rgb : 0, 0, 0;
--ion-color-secondary-shade : #649FA1;
--ion-color-secondary-tint : #80BCBE;
--ion-color-tertiary : #F1FCFC;
--ion-color-tertiary-rgb : 241, 252, 252;
--ion-color-tertiary-contrast : #000000;
--ion-color-tertiary-contrast-rgb : 0, 0, 0;
--ion-color-tertiary-shade : #D4DEDE;
--ion-color-tertiary-tint : #F2FCFC;
--ion-color-success : #10DC60;
--ion-color-success-rgb : 16, 220, 96;
--ion-color-success-contrast : #FFFFFF;
--ion-color-success-contrast-rgb : 255, 255, 255;
--ion-color-success-shade : #0EC254;
--ion-color-success-tint : #28E070;
--ion-color-warning : #FFCE00;
--ion-color-warning-rgb : 255, 206, 0;
--ion-color-warning-contrast : #FFFFFF;
--ion-color-warning-contrast-rgb : 255, 255, 255;
--ion-color-warning-shade : #E0B500;
--ion-color-warning-tint : #FFD31A;
--ion-color-danger : #F04141;
--ion-color-danger-rgb : 245, 61, 61;
--ion-color-danger-contrast : #FFFFFF;
--ion-color-danger-contrast-rgb : 255, 255, 255;
--ion-color-danger-shade : #D33939;
--ion-color-danger-tint : #F25454;
--ion-color-dark : #222428;
--ion-color-dark-rgb : 34, 34, 34;
--ion-color-dark-contrast : #FFFFFF;
--ion-color-dark-contrast-rgb : 255, 255, 255;
--ion-color-dark-shade : #1E2023;
--ion-color-dark-tint : #383A3E;
--ion-color-medium : #989AA2;
--ion-color-medium-rgb : 152, 154, 162;
--ion-color-medium-contrast : #FFFFFF;
--ion-color-medium-contrast-rgb : 255, 255, 255;
--ion-color-medium-shade : #86888F;
--ion-color-medium-tint : #A2A4AB;
--ion-color-light : #F4F5F8;
--ion-color-light-rgb : 244, 244, 244;
--ion-color-light-contrast : #000000;
--ion-color-light-contrast-rgb : 0, 0, 0;
--ion-color-light-shade : #D7D8DA;
--ion-color-light-tint : #F5F6F9;
}
//#media (prefers-color-scheme: dark) {
// /*
// * Dark Colors
// * -------------------------------------------
// */
//
// body {
// --ion-color-primary: #428cff;
// --ion-color-primary-rgb: 66,140,255;
// --ion-color-primary-contrast: #ffffff;
// --ion-color-primary-contrast-rgb: 255,255,255;
// --ion-color-primary-shade: #3a7be0;
// --ion-color-primary-tint: #5598ff;
//
// --ion-color-secondary: #50c8ff;
// --ion-color-secondary-rgb: 80,200,255;
// --ion-color-secondary-contrast: #ffffff;
// --ion-color-secondary-contrast-rgb: 255,255,255;
// --ion-color-secondary-shade: #46b0e0;
// --ion-color-secondary-tint: #62ceff;
//
// --ion-color-tertiary: #6a64ff;
// --ion-color-tertiary-rgb: 106,100,255;
// --ion-color-tertiary-contrast: #ffffff;
// --ion-color-tertiary-contrast-rgb: 255,255,255;
// --ion-color-tertiary-shade: #5d58e0;
// --ion-color-tertiary-tint: #7974ff;
//
// --ion-color-success: #2fdf75;
// --ion-color-success-rgb: 47,223,117;
// --ion-color-success-contrast: #000000;
// --ion-color-success-contrast-rgb: 0,0,0;
// --ion-color-success-shade: #29c467;
// --ion-color-success-tint: #44e283;
//
// --ion-color-warning: #ffd534;
// --ion-color-warning-rgb: 255,213,52;
// --ion-color-warning-contrast: #000000;
// --ion-color-warning-contrast-rgb: 0,0,0;
// --ion-color-warning-shade: #e0bb2e;
// --ion-color-warning-tint: #ffd948;
//
// --ion-color-danger: #ff4961;
// --ion-color-danger-rgb: 255,73,97;
// --ion-color-danger-contrast: #ffffff;
// --ion-color-danger-contrast-rgb: 255,255,255;
// --ion-color-danger-shade: #e04055;
// --ion-color-danger-tint: #ff5b71;
//
// --ion-color-dark: #f4f5f8;
// --ion-color-dark-rgb: 244,245,248;
// --ion-color-dark-contrast: #000000;
// --ion-color-dark-contrast-rgb: 0,0,0;
// --ion-color-dark-shade: #d7d8da;
// --ion-color-dark-tint: #f5f6f9;
//
// --ion-color-medium: #989aa2;
// --ion-color-medium-rgb: 152,154,162;
// --ion-color-medium-contrast: #000000;
// --ion-color-medium-contrast-rgb: 0,0,0;
// --ion-color-medium-shade: #86888f;
// --ion-color-medium-tint: #a2a4ab;
//
// --ion-color-light: #222428;
// --ion-color-light-rgb: 34,36,40;
// --ion-color-light-contrast: #ffffff;
// --ion-color-light-contrast-rgb: 255,255,255;
// --ion-color-light-shade: #1e2023;
// --ion-color-light-tint: #383a3e;
// }
//
// /*
// * iOS Dark Theme
// * -------------------------------------------
// */
//
// .ios body {
// --ion-background-color: #000000;
// --ion-background-color-rgb: 0,0,0;
//
// --ion-text-color: #ffffff;
// --ion-text-color-rgb: 255,255,255;
//
// --ion-color-step-50: #0d0d0d;
// --ion-color-step-100: #1a1a1a;
// --ion-color-step-150: #262626;
// --ion-color-step-200: #333333;
// --ion-color-step-250: #404040;
// --ion-color-step-300: #4d4d4d;
// --ion-color-step-350: #595959;
// --ion-color-step-400: #666666;
// --ion-color-step-450: #737373;
// --ion-color-step-500: #808080;
// --ion-color-step-550: #8c8c8c;
// --ion-color-step-600: #999999;
// --ion-color-step-650: #a6a6a6;
// --ion-color-step-700: #b3b3b3;
// --ion-color-step-750: #bfbfbf;
// --ion-color-step-800: #cccccc;
// --ion-color-step-850: #d9d9d9;
// --ion-color-step-900: #e6e6e6;
// --ion-color-step-950: #f2f2f2;
//
// --ion-toolbar-background: #0d0d0d;
//
// --ion-item-background: #1c1c1c;
// --ion-item-background-activated: #313131;
// }
//
//
// /*
// * Material Design Dark Theme
// * -------------------------------------------
// */
//
// .md body {
// --ion-background-color: #121212;
// --ion-background-color-rgb: 18,18,18;
//
// --ion-text-color: #ffffff;
// --ion-text-color-rgb: 255,255,255;
//
// --ion-border-color: #222222;
//
// --ion-color-step-50: #1e1e1e;
// --ion-color-step-100: #2a2a2a;
// --ion-color-step-150: #363636;
// --ion-color-step-200: #414141;
// --ion-color-step-250: #4d4d4d;
// --ion-color-step-300: #595959;
// --ion-color-step-350: #656565;
// --ion-color-step-400: #717171;
// --ion-color-step-450: #7d7d7d;
// --ion-color-step-500: #898989;
// --ion-color-step-550: #949494;
// --ion-color-step-600: #a0a0a0;
// --ion-color-step-650: #acacac;
// --ion-color-step-700: #b8b8b8;
// --ion-color-step-750: #c4c4c4;
// --ion-color-step-800: #d0d0d0;
// --ion-color-step-850: #dbdbdb;
// --ion-color-step-900: #e7e7e7;
// --ion-color-step-950: #f3f3f3;
//
// --ion-item-background: #1A1B1E;
// }
//
// ion-title.title-large {
// --color: white;
// }
//}
Hope to help you
One way to do this is to force light styles by using media queries. On devices that prefer light, you can supply your dark theme variables, but you need to override #media (prefers-color-scheme: dark) scope with your light theme variables.
One way to do this is to use a mixin for your theme, and include it in both :root scope and #media (prefers-color-scheme: dark).
Conversely, if you want to force dark theme, use a mixin for your theme, and include it in both :root scope and #media (prefers-color-scheme: light).
Note: Ionic (ionic/cli#6.10.0) and SCSS
/**
* {#mixin} - Supplies dark theme variables to the included scope
*/
#mixin eternal-darkness() {
// Dark theme variables...
}
:root {
#include eternal-darkness;
}
#media (prefers-color-scheme: light) {
#include eternal-darkness;
}
In my web browser, the theme was light. Which is what I wanted.
In the ios emulator, it was dark. To make it light I did this:
In variables.css
#media (prefers-color-scheme: lights) {
...
}
Simply have this tag placed in index.html file
<meta name="color-scheme" content="light dark" />
It works for me! Thanks

Resizing in interact.js

I am trying to learn how to use the interact.js library and I cant get the resizing example to be draggable. I can resize the div ".resize-drag" but I don´t know how to get it draggable. Can anyone tell me is wrong with my code?
This code is only so that I can learn to implement the resize example provided at http://interactjs.io/ So far I´ve tried using npm instead of the script tag. When I copied the example below from the top of the interact.js website and renamed the element ".item" in the interact claus but that did not work
interact('.item').draggable({
onmove(event) {
console.log(event.pageX,
event.pageY)
}
})
I suspected it might be a syntax error so I also tried adding semicolon behind the function but that didn´t seem to be the problem. Please have a look at my entire code below to see what I have done wrong.
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project
Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
.resize-drag {
background-color: #29e;
color: white;
font-size: 20px;
font-family: sans-serif;
border-radius: 8px;
padding: 20px;
margin: 30px 20px;
width: 120px;
/* This makes things *much* easier */
box-sizing: border-box;
}
.resize-container {
display: inline-block;
width: 100%;
height: 240px;
background-color: lightblue;
}
</style>
<script
src="https://unpkg.com/interactjs#1.3.4/dist/interact.min.js"></script>
<script type="text/javascript">
interact('.resize-drag').draggable({
onmove(event) {
console.log(event.pageX,
event.pageY)
}
})
interact('.resize-drag')
.draggable({
onmove: window.dragMoveListener,
restrict: {
restriction: 'parent',
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.resizable({
// resize from all edges and corners
edges: { left: true, right: true, bottom: true, top: true },
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: { width: 100, height: 50 },
},
inertia: true,
})
.on('resizemove', function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0),
y = (parseFloat(target.getAttribute('data-y')) || 0);
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.webkitTransform = target.style.transform =
'translate(' + x + 'px,' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
target.textContent = Math.round(event.rect.width) + '\u00D7' +
Math.round(event.rect.height);
});
</script>
</head>
<body>
<div class="resize-container">
<div class="resize-drag">
Resize from any edge or corner
</div>
</div>
</body>
</html>
I want to be able to drag the div and not just resize it.
I recently struggled with that as well, and it turned out that the 'resizing' code blurbs don't have all the code. The js is missing window.dragMoveListener and dragMoveListener, which is found in the 'dragging' section.
Specifically you need to add this
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
// this is used later in the resizing and gesture demos
window.dragMoveListener = dragMoveListener;

google bubble chart how to add HTML tooltip

I have a Google chart: bubble chart.
I want to add a custom HTML tooltip, with the specified value relative to the point:
<div class="clearfix>
<h3>Metric: []</h3>
<h4>ID comes here: []</h4>
<h4>X Axis Value comes here: []</h4>
<h4>Y Axis Value comes here: []</h4>
<h4>Volume comes here: []</h4>
</div>
Currently it shows a default tooltip, which is not arranged in the way i want. And I cannot edit the fields also.
I want to use Custom HTML tooltip, but sadly it is not supported by Google charts in bubble chart as of yet.
Any way to achieve the same.
MY CODE
JSFIDDLE Demo
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("visualization", "1", {
packages: ["corechart"]
});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
["ID", "X Axis Value", "Y Axis Value", "Metric", "Volume"],
["Range: 2-5", 3, 2.5, "Value Provider", 300],
["Range: 2-5", 4, 2.5, "Third Provider", 239],
["Range: 3-8", 3, 7.4, "Second Provider", 344],
["Range: 5-8", 5, 7.3, "Value Provider", 324],
["Range: 2-10", 9, 2.32, "Third Provider", 765],
["Range: 2-5", 5, 3, "Value Provider", 342],
]);
var options = {
title: 'Range Volume',
hAxis: {
title: 'X Axis'
},
vAxis: {
title: 'Y Axis'
},
bubble: {
textStyle: {
fontSize: 11,
color:'transparent'
}
}
};
var chart = new google.visualization.BubbleChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="chart_div" style="width: 100%; height: 90vh;"></div>
</body>
Basically you need some kind of mousetracker to know where tooltip should be shown and you need two listeners like this:
google.visualization.events.addListener chart, 'onmouseover', mouseoverHandler
google.visualization.events.addListener chart, 'onmouseout', mouseoutHandler
and you should add id='tooltip' to your tooltip with css like:
#tooltip {
display: none;
position: absolute;
padding: 10px;
border: 1px solid #ddd;
background: white;
width: 350px;
-webkit-box-shadow: 0 0 5px #ddd;
-moz-box-shadow: 0 0 5px #ddd;
box-shadow: 0 0 5px #ddd;
z-index: 1;
}
javascript:
var $tooltip = $('#tooltip')
mouseoverHandler = function(event) {
metric = data.getValue(event.row, 3);
id = data.getValue(event.row, 0);
xAxis = data.getValue(event.row, 1);
yAxis = data.getValue(event.row, 2);
volume = data.getValue(event.row, 4);
$tooltip.find('h3').append(metric);
$tooltip.css({
top: y,
left: x
}).show();
};
mouseoutHandler = function() {
$tooltip.hide();
};
x and y are your mouse cords taken from some kind of mouse tracker like: Javascript - Track mouse position.
title = data.getValue(event.row, 3); is line where you take data from your data from your chart and you have to insert this data into your tooltip the way you want it to be inserted. I hope it will help.

SVG special Characters like <sup>2</sup> or &$178; or ²

I am trying to put on SVG the special character ( 2 ) ( --> ² ) with no result. Is there a way to put unicode characters on SVG ?
Here is an example code using highcharts 2.1.6
<div id="container" style="height: 300px"></div>​
$(function() {
var renderer = new Highcharts.Renderer(
$('#container')[0],
400,
300);
renderer.text(100, 100, 100, 100, 5).attr({
'stroke-width': 2,
zIndex: 3,
rotation: 170,
text: 'sdfsdf²'
}).add();
});​
Thanks a lot.