How to use GM_addStyle in Tampermonkey on #shadow-root (open)? - tampermonkey

There are extensions that make small on-page popups that in Inspection in Chrome show as #shadow-root(open).
I want to customise its styles with Tampermonkey, but using GM_addStyle(".class_name{zoom: 166%;}"); doesn't work. On native elements it works.

GM_addStyle() appends the <style> element it creates to the <head>, therefore it can't modify the styling of Shadow DOM elements. You need to use GM_addElement().
// ==UserScript==
// #name Shadow DOM styling
// #version 0.1
// #author double-beep
// #match http*://example.com
// #grant GM_addElement
// ==/UserScript==
(function() {
'use strict';
const shadowRoot = document.querySelector('element');
const styles = `div { color: red; }`;
GM_addElement(shadowRoot, 'style', { textContent: styles });
})();
If the styles aren't applied, you can create a <script> element instead in a similar way:
GM_addElement(shadowRoot, 'script', {
textContent: 'shadowRootElement.shadowRoot.querySelector("...").style.color = "red";'
});

You need to insert the style tag into the shadow root.
(pinching some code from this answer)
function addStyleToShadowRoot(shadowRoot,css) {
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
shadowRoot.appendChild(style);
}
Call it like this:
addStyleToShadowRoot(customElement.shadowRoot, myCss);

Related

Insert raw html code to quill

Is there option to insert raw html code to quill?
quill.insertText();
quill.clipboard.dangerouslyPasteHTML()
both are parsed by matcher but I need to paste exactly formatted html code for an email footer.
If the footer content is meant to be static and un-editable, you can do this by extending the BlockEmbed blot then adding a button for your new format in the toolbar. There are 2 different ways to handle what HTML get's entered into the new format.
1. Let the user enter the HTML to embed:
// Import the BlockEmbed blot.
var BlockEmbed = Quill.import('blots/block/embed');
// Create a new format based off the BlockEmbed.
class Footer extends BlockEmbed {
// Handle the creation of the new Footer format.
// The value will be the HTML that is embedded.
// By default, the toolbar will show a prompt window to get the value.
static create(value) {
// Create the node using the BlockEmbed's create method.
var node = super.create(value);
// Set the srcdoc attribute to equal the value which will be your html.
node.setAttribute('srcdoc', value);
// Add a few other iframe fixes.
node.setAttribute('frameborder', '0');
node.setAttribute('allowfullscreen', true);
node.setAttribute('width', '100%');
return node;
}
// return the srcdoc attribute to represent the Footer's value in quill.
static value(node) {
return node.getAttribute('srcdoc');
}
}
// Give our new Footer format a name to use in the toolbar.
Footer.blotName = 'footer';
// Give it a class name to edit the css.
Footer.className = 'ql-footer';
// Give it a tagName of iframe to tell quill what kind of element to create.
Footer.tagName = 'iframe';
// Lastly, register the new Footer format so we can use it in our editor.
Quill.register(Footer, true);
var quill = new Quill('#editor-container', {
modules: {
toolbar: {
container: ['footer'] // Toolbar with just our footer tool (of course you can add all you want).
}
},
theme: 'snow'
});
.ql-toolbar .ql-footer:before {
content: 'footer';
}
.ql-editor .ql-footer {
background: #f7f7f7;
}
<link href="//cdn.quilljs.com/1.3.4/quill.core.css" rel="stylesheet"/>
<link href="//cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"/>
<div id="editor-container">
<h1>Test Content</h1>
<p>Enter a footer</p>
</div>
<script src="//cdn.quilljs.com/1.3.4/quill.min.js"></script>
2. Use specific HTML
// Import the BlockEmbed blot.
var BlockEmbed = Quill.import('blots/block/embed');
// Create a new format based off the BlockEmbed.
class Footer extends BlockEmbed {
// Handle the creation of the new Footer format.
// The value will be the HTML that is embedded.
// This time the value is passed from our custom handler.
static create(value) {
// Create the node using the BlockEmbed's create method.
var node = super.create(value);
// Set the srcdoc attribute to equal the value which will be your html.
node.setAttribute('srcdoc', value);
// Add a few other iframe fixes.
node.setAttribute('frameborder', '0');
node.setAttribute('allowfullscreen', true);
node.setAttribute('width', '100%');
return node;
}
// return the srcdoc attribute to represent the Footer's value in quill.
static value(node) {
return node.getAttribute('srcdoc');
}
}
// Give our new Footer format a name to use in the toolbar.
Footer.blotName = 'footer';
// Give it a class name to edit the css.
Footer.className = 'ql-footer';
// Give it a tagName of iframe to tell quill what kind of element to create.
Footer.tagName = 'iframe';
// Register the new Footer format so we can use it in our editor.
Quill.register(Footer, true);
// Specify the HTML that will be embedded.
var footerHTML = '<h1>Footer</h1>'
+ '<p>This is our new footer</p>';
// Create the footer handler.
var footerHandler = function() {
// Get the cursor location to know where footer will be added.
var index = this.quill.getSelection(true).index;
// Insert the footer with the footerHTML.
this.quill.insertEmbed(index, 'footer', footerHTML);
};
// Import the Toolbar module so we can add a custom handler to our footer button.
var Toolbar = Quill.import('modules/toolbar');
// Add our custom footer handler to the footer button.
Toolbar.DEFAULTS.handlers['footer'] = footerHandler;
var quill = new Quill('#editor-container', {
modules: {
toolbar: {
container: ['footer'] // Toolbar with just our footer tool (of course you can add all you want).
}
},
theme: 'snow'
});
.ql-toolbar .ql-footer:before {
content: 'footer';
}
.ql-editor .ql-footer {
background: #f7f7f7;
}
<link href="//cdn.quilljs.com/1.3.4/quill.core.css" rel="stylesheet"/>
<link href="//cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"/>
<div id="editor-container">
<h1>Test Content</h1>
<p>Enter a footer</p>
</div>
<script src="//cdn.quilljs.com/1.3.4/quill.min.js"></script>

How to trigger animations in a React app based on the scroll position

Let's say I need to add an element to the navbar when the user have scrolled past the header of the site. How can I do something like this in React without using jQuery?
You can do some thing like this: (this function was copied from my own react-sticky-dynamic-header that I created before: https://github.com/thinhvo0108/react-sticky-dynamic-header )
componentDidMount() {
var h1 = parseInt(this.refs.header.offsetHeight);
window.addEventListener('scroll', this._calcScroll.bind(this, h1));
}
componentWillUnmount() {
window.removeEventListener('scroll', this._calcScroll)
}
_calcScroll(h1) {
var _window = window;
var heightDiff = parseInt(h1);
var scrollPos = _window.scrollY;
if (scrollPos > heightDiff) {
// here this means user has scrolled past your header,
// you may rerender by setting State or do whatever
this.setState({
//stateKey: stateValue,
});
} else {
// here the user has scrolled back to header's territory,
// it's optional here for you to remove the element on navbar as stated in the question or not
this.setState({
//stateKey: stateValue,
});
}
}
render() {
return (
<div ref="header">YOUR HEADER HERE</div>
);
}
For a smooth animation when your element added or removed from the navbar, you can just add this into the element's CSS style:
#your-element{
transition: opacity 0.3s ease-in;
}
You can try to install my library to see if it can extend your needs:
https://www.npmjs.com/package/react-sticky-dynamic-header
Feel free to post here some errors if any, thanks

Combining <ons-sliding-menu> and <ons-carousel>

I have an app with <ons-sliding-menu> and a page with <ons-toolbar> and a horizontal <ons-carousel> covering the remaining space.
For the <ons-sliding-menu> the parameter swipe-target-width="50px" is set.
Is there a way to tell the <ons-carousel> to ignore events originating from the most left 50px and let these go to the menu?
Currently there is no option to make the carousel ignore events on one side, but perhaps you can make a trick. You can put a div at the same level than the carousel and let it take the clicks instead of the carousel in the area you need:
<div class="cover"></div>
<ons-carousel>
...
</ons-carousel>
You can change these values to fit your case:
.cover {
position: absolute;
left: 0;
height: 100%;
width: 200px;
z-index: 1;
}
Check it out here: http://codepen.io/frankdiox/pen/YqKOJE
Hope it helps!
After some experimentation, I came to the solution to inject the necessary functionality directly in the drag event handlers of the OnsCarouselElement. For this purpose I have introduced the attribute swipe-ignore-left for the <ons-carousel>. The other sites could easily be added when needed.In order to inject the functionality, load this JS-Code after loading onsenui.js:
(function () {
'use strict';
/****************************************************************
Checks the current event against the attribute swipe-ignore-left.
****************************************************************/
window.OnsCarouselElement.prototype._ignoreDrag = function (event) {
var attr = this.getAttribute('swipe-ignore-left');
if (attr === undefined) return false;
var left = parseInt(attr, 10);
if (left === undefined || left < 1) return false;
var startX = event.gesture.center.clientX - event.gesture.deltaX;
return startX < left;
};
/****************************************************************
Save the original drag-event-handlers
****************************************************************/
var originalCarouselOnDrag = window.OnsCarouselElement.prototype._onDrag;
var originalCarouselOnDragEnd = window.OnsCarouselElement.prototype._onDragEnd;
/****************************************************************
Override: OnsCarouselElement.prototype._onDrag
****************************************************************/
window.OnsCarouselElement.prototype._onDrag = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDrag.apply(this, arguments);
};
/****************************************************************
Override: OnsCarouselElement.prototype._onDragEnd
****************************************************************/
window.OnsCarouselElement.prototype._onDragEnd = function (event) {
if (this._ignoreDrag(event)) return;
originalCarouselOnDragEnd.apply(this, arguments);
};
})();
To preserve for example the left 20 pixel for the <ons-sliding-menu>, this HTML is to provide:
<ons-sliding-menu ... side="left" swipeable swipe-target-width="20px" />
...
<ons-carousel ... swipeable swipe-ignore-left="20px" />

How to use $mdTheming to apply theme to custom element

I've been trying to figure out a way to use $mdTheming service provided by angular-material library to apply theme configured through $mdThemingProvider.
In Simpler terms:
Consider an element
<div class="md-primary" theme></div>
The theme directive in this case has to inject the primary color configure to the theme.
Below is the approach I tried to use, after going through angular-material's code
Just to see how they might have done it
export function ThemeDirective($mdTheming) {
'ngInject';
let directive = {
restrict: 'A',
link: (scope, element) => {
$mdTheming(element);
}
};
return directive
}
The above code doesn't seem to be doing anything. I'm sure I've overlooked something. Need help.
This may help in your research... you can use it towards applying a theme to your directive. I started with the link https://material.angularjs.org/latest/Theming/05_under_the_hood
When you look further into the angular code, you'll find the constant $MD_THEME_CSS. This is basically a css file with all the styles for each of their directives - but it is formatted with tags that they interpolate:
...
md-input-container.md-THEME_NAME-theme .md-input {
color: {{foreground-1}};
border-color: {{foreground-4}};
text-shadow: {{foreground-shadow}};
}
md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder,md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder,md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder {
color: {{foreground-3}};
}
...
These tags they replace with color values are shown in the documentation of the mdThemingProvider...
/* Some Example Valid Theming Expressions
* =======================================
* Intention group expansion: (valid for primary, accent, warn, background)
* {{primary-100}} - grab shade 100 from the primary palette
* {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
* {{primary-100-contrast}} - grab shade 100's contrast color
* {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
* {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
* {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
* {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
* {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
* {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
*
* Foreground expansion: Applies rgba to black/white foreground text
* {{foreground-1}} - used for primary text
* {{foreground-2}} - used for secondary text/divider
* {{foreground-3}} - used for disabled text
* {{foreground-4}} - used for dividers
*/
This string is then formatted at run time by generateAllThemes() after all the themes have been defined. This will inject these styles into the <head> element - as you can see by inspecting your page in chrome:
enter image description here
Now I've never done this before personally so i don't know the standards here, and cannot find it documented. But I'm assuming you can call GenerateTheme() somehow to generate your own styles to use in your html... or maybe just borrow some classes that have already been generated for the core code.
I have however done something similar using my own services rather than theirs.
Here's a possible start to your solution that I've used in the past... I wrote a simple provider to save my theme palette after i've defined the theme in my .config(). Then i wrote a service to connect the theme color codes to the actual rgb colors. Hopefully it's not too hacky.
(function() {
var app = angular.module('MyApp', ['ngMaterial', 'ngMessages'])
// The provider... I store the color palette here since a service isn't available during .config();
.provider('colorPalette', function colorPaletteProvider() {
var _PALETTE = {};
this.SetPalette = function(value) {
_PALETTE = value
}
this.$get = [
function() {
return _PALETTE;
}
];
})
.config(function($mdThemingProvider, colorPaletteProvider) {
var xoMap = $mdThemingProvider.extendPalette('purple', {
'500': '833A96'
});
$mdThemingProvider.definePalette('XO-Main', xoMap);
// add a couple of themes
$mdThemingProvider.theme('default')
.primaryPalette('XO-Main')
.accentPalette('pink', {
"default": "500",
"hue-1": "50"
})
.backgroundPalette('grey');
$mdThemingProvider.theme('order')
.primaryPalette('XO-Main')
.accentPalette('light-blue', {
"default": "500",
"hue-1": "50"
});
// save the palette so i can see it later
colorPaletteProvider.SetPalette($mdThemingProvider._PALETTES);
})
.run(function($interpolate, themeColorsService) {
// inject some styles into the head
var orderTheme = themeColorsService.GetColors("order");
var myStyle = $interpolate(".nav-icon-order {color: {{accent.default.bg}};}")(orderTheme);
console.debug(myStyle);
themeColorsService.AddStyle(myStyle);
themeColorsService.AddStyle($interpolate("md-toolbar.hpbx-toolbar-accent-order, .panel-heading.hpbx-toolbar-accent-order { border-bottom: 5px solid {{accent.default.bg}};}")(orderTheme));
});
// The controller
app.controller("AppCtrl", function($scope, themeColorsService) {
$scope.themeColors = themeColorsService.GetColors("default");
});
})();
// example directive where the theme is passed in
angular.module('MyApp').directive('theme', function (themeColorsService) {
return {
restrict: "A",
transclude: true,
template: "<div ng-style='{color: themeColors.primary.default.bg}' ng-transclude></div>",
scope: {
themeName: "="
},
controller: function ($scope, $element, $attrs) {
$scope.themeColors = themeColorsService.GetColors("default");
}
}
});
// The service
angular.module('MyApp').service("themeColorsService", function(colorPalette, $mdTheming) {
this.themes = {};
// tie the color codes together with the palettes
this.GetColors = function(theme) {
var returnVal = {};
if (!this.themes.hasOwnProperty(theme)) {
theme = $mdTheming.THEMES[theme];
_.keys(theme.colors).forEach(function(key) {
returnVal[key] = {};
var palette = theme.colors[key].name;
if (!_.isUndefined(palette)) {
_.keys(theme.colors[key].hues).forEach(function(hue) {
var c = theme.colors[key].hues[hue];
var p = colorPalette[palette][c];
returnVal[key][hue] = {};
returnVal[key][hue].bg = getRGB(p.value);
returnVal[key][hue].fg = getRGB(p.contrast);
});
}
});
this.themes[theme] = _.cloneDeep(returnVal);
}
return this.themes[theme];
};
var getRGB = function(value) {
var returnVal = "";
if (value.length == 4) {
returnVal = "rgba(" + value[0] + "," + value[1] + "," + value[2] + "," + value[3] + ")";
} else if (value.length == 3) {
returnVal = "rgb(" + value[0] + "," + value[1] + "," + value[2] + ")";
}
return returnVal;
};
// insert a style into the head element
this.AddStyle = function(styleContent) {
var head = document.head;
var firstChild = head ? head.firstElementChild : null;
var style = document.createElement('style');
style.setAttribute('xo-theme-style', '');
style.appendChild(document.createTextNode(styleContent));
head.insertBefore(style, firstChild);
};
});
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-animate.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.6/angular-material.css" rel="stylesheet" />
<html ng-app="MyApp">
<body>
<div ng-controller="AppCtrl">
<div>I'm unstyled</div>
<!-- style using ng-style -->
<div ng-style="{color: themeColors.primary.default.bg}">I'm styled with ngStyle</div>
<!-- use your injected css style -->
<div class="nav-icon-order">I'm using an injected css class</div>
<!-- send it to a directive the way you listed in your example -->
<div theme>This is a styled directive</div>
<!-- show the themeColors object -->
<pre>themeColors:{{themeColors | json}}</pre>
</div>
</body>
</html>

When zooming, circle gets 'dragged' from original position

I'm using mapbox.js and map.css to create a map with a simple circle drawn on it, and nothing else. (working jsfiddle) On the example that I did without angular, the zoom works as it should (meaning, it stays on the same position when zooming).
When I integrated with the example and I zoom, the circle gets dragged slightly to the top left when zooming out and to the bottom right when I zoom in, in the end returning to the original position when the zoom stops.
On the angular project I'm using require to load the scripts to the page and the view that has the map is not the first one that is loaded. I am not using a directive for leaflet, just using the files. The code is exactly the same from the example to the angular project, the only difference is that I am not using a script tag in the html file as I was in the example (since the code has been moved to the controller).
I wanted the behaviour to be the same as it was in the example but at this stage I don't know what might be causing it besides the fact that is not the view that is first being loaded.
Here's the view code:
<div id="leafletMap" style="width: 500px; height: 300px;"></div>
Here's the code in my controller:
/* globals L, $ */
define(['angular'], function (angular) {
'use strict';
/**
* #ngdoc function
* #name rmsPortalApp.controller:ViewMapCtrl
* #description
* # ViewMapCtrl
* Controller of the rmsPortalApp
*/
angular.module('rmsPortalApp.controllers.ViewMapCtrl', [])
.controller('ViewMapCtrl', function ($scope, Data) {
var ownerCircleLayer;
L.Map = L.Map.extend({
openPopup: function (popup) {
// this.closePopup(); // just comment this
this._popup = popup;
return this.addLayer(popup).fire('popupopen', {
popup: this._popup
});
}
});
var map = L.map('leafletMap', {
touchZoom: true,
dragging: true
}).setView([39.678, -8.229], 6);
/* Initialize the SVG layer */
//map._initPathRoot();
/* SVG from the map object */
//var svg = d3.select("#map").select("svg");
var southWest = [85.05, -180.01];
var northEast = [-85.06, 180.05];
var bounds = L.latLngBounds(southWest, northEast);
map.setMaxBounds(bounds);
L.tileLayer('http://api.tiles.mapbox.com/v4/bmpgp.010fd6a5/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiYm1wZ3AiLCJhIjoiOWY4NGYwN2VjZDg0MGI1ZjdmMWI3ZjdlNGNmY2NmNmQifQ.FZ5cr4mO3iDKVkx9zz4Nkg', {
attribution: '© Powered By CGI',
minZoom: 3,
maxZoom: 18,
id: 'bmpgp.010fd6a5',
accessToken: 'pk.eyJ1IjoiYm1wZ3AiLCJhIjoiOWY4NGYwN2VjZDg0MGI1ZjdmMWI3ZjdlNGNmY2NmNmQifQ.FZ5cr4mO3iDKVkx9zz4Nkg'
}).addTo(map);
setOwnerCircle()
function setOwnerCircle() {
ownerCircleLayer = new L.layerGroup();
var ownerCircle = L.circle([39, -8], 3000, {
color: 'red',
opacity: 1,
weigth: 1,
fillOpacity: 0.0,
className: 'leaflet-zoom-animated'
});
ownerCircleLayer.addLayer(ownerCircle);
var circleBounds = ownerCircle.getBounds();
// Image popup
var endLat = circleBounds.getCenter().lat - 0.10000;
var endLon = circleBounds.getEast();
map.addLayer(ownerCircleLayer);
addedOwnerCircle = true;
}
})
});
EDIT:
I have tried putting it in the first view that is being loaded, it still happens.
Found the reason why it was happening:
When double checking the actual mapbox.js and mapbox.css I noticed that my css was wrong, upon replacing it with the right mapbox.css it stopped doing the dragging. Thank you for the help regardless.

Resources