Quill strips out simple html when dangerouslyPasteHTML into editor - html

<style>
#editor-container {
height: 375px;
}
.link {
color:blue;
}
</style>
<div id="editor-container">
This is a test
</div>
<script type="text/javascript">
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Compose an epic...',
theme: 'bubble' // or 'bubble'
});
quill.clipboard.dangerouslyPasteHTML(5, "<span class=\"link\" data-test=\"test\">testing</span>", "silent");
</script>
MVCE - https://codepen.io/anon/pen/QMQMee
The HTML get stripped out despite being pretty harmless (this will be handled better later).

My current plan, due to the way Quill does not allow pasted html, is (As part of a click action on the mentioned person's name):
$("#tag-selectable-users-list li").on("click",
function() {
var $this = $(this);
var startIndex = $this.data("data-start-index");
var userName = $this.data("data-user-name");
var userId = $this.data("data-user-id");
var taggedUserIds = $("#hiddenTaggedUsers");
taggedUserIds.val((taggedUserIds.val()||"") + ";" + userId);
var delta = [];
if (startIndex > 0) {
//retain up to the tag start
delta.push({ retain: parseInt(startIndex) });
}
//delete the junk
delta.push({ delete: tagStatus.Total.length });
//insert the new characters
delta.push({
insert: "##" + userName,
attributes: {
color: "blue",
underline: "true"
}
});
//insert a blank space to end the span
delta.push({ insert: " " });
quill.updateContents(delta,
'api');
});
}

Related

Chrome extension web inspector

I want to write a chrome extension where it is possible to mark features on a website and save it in a table, for this, I want to use the Chrome web inspector.
Unfortunately, I am new to this area (chrome plugins) and therefore I am looking for help (links, tutorials, related work etc.) to use the web inspector in my own extension.
Simple example on this website https://ieeexplore.ieee.org/document/1005630.
My idea is to mark for example the date of publication, and the plugin write the complete div to a table.
actually, I found a simple solution.
Sample
http://g.recordit.co/5CCFjXpe8J.gif
It's only a small part of my tool to keep it simple.
The main idea comes from Google Chrome Extension: highlight the div that the mouse is hovering over
'iframe' is the injected sidebar
marker.js contains the script to mark divs
manifest.json
{
"name": "Feature extractor",
"version": "1.0",
"description": "Feature extractor from website",
"permissions": ["activeTab", "declarativeContent", "storage", "contextMenus", "<all_urls>", "tabs"],
"browser_action": {},
"web_accessible_resources": ["iframe.html","iframe.js"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": [
"marker.css"
],
"js": [
"js/jquery-1.8.3.min.js",
"marker.js"
]
}
],
"manifest_version": 2
}
background.js
'use strict';
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostEquals: 'developer.chrome.com'},
})],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
// sidebar
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,"toggle");
})
});
// message passing
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
console.log(request);
callback({'request':request});
});
// context menu
var labels = ['author','date','abstract']
for(var label in labels) {
console.log(labels[label])
chrome.contextMenus.create({id: labels[label], "title": labels[label], "contexts":['all']});
}
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == labels[0]) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,labels[0]);
})
}
});
iframe.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/jquery.mobile-1.2.1.min.css" />
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/jquery.mobile-1.2.1.min.js"></script>
<script src="iframe.js"></script>
</head>
<body>
<button id="send">
send
</button>
<div id="responses">
</div>
</body>
</html>
I need the jQuery.fn.. script to identify the selected div Get unique selector of element in Jquery
iframe.js
// unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
window.addEventListener('DOMContentLoaded', function () {
var callback = function (data) {
$("#responses").append("<div>" + data + "</div>");
};
var send = function () {
chrome.runtime.sendMessage(Date(), callback);
}
chrome.runtime.onMessage.addListener(function(msg, data){
if (msg.command == "append-author") {
$("#responses").append("<div>" + msg.el + "</div>")
}
})
document.getElementById('send').addEventListener('click', send);
});
Google Chrome Extension: highlight the div that the mouse is hovering over
marker.js
// Unique ID for the className.
var MOUSE_VISITED_CLASSNAME = 'crx_mouse_visited';
var MOUSE_MARKED_CLASSNAME = 'crx_mouse_marked';
// Previous dom, that we want to track, so we can remove the previous styling.
var prevDOM = null;
// Mouse listener for any move event on the current document.
document.addEventListener('mousemove', function (e) {
let srcElement = e.srcElement;
// Lets check if our underlying element is a IMG.
if (prevDOM != srcElement && srcElement.nodeName == 'DIV' ) {
// For NPE checking, we check safely. We need to remove the class name
// Since we will be styling the new one after.
if (prevDOM != null) {
prevDOM.classList.remove(MOUSE_VISITED_CLASSNAME);
}
// Add a visited class name to the element. So we can style it.
srcElement.classList.add(MOUSE_VISITED_CLASSNAME);
// The current element is now the previous. So we can remove the class
// during the next ieration.
prevDOM = srcElement;
// console.info(srcElement.currentSrc);
// console.dir(srcElement);
}
}, false);
var iframe = document.createElement('iframe');
iframe.style.background = "green";
iframe.id = "comm-test-container";
iframe.style.height = "100%";
iframe.style.width = "0px";
iframe.style.position = "fixed";
iframe.style.top = "0px";
iframe.style.right = "0px";
iframe.style.zIndex = "9000000000000000000";
iframe.frameBorder = "none";
iframe.src = chrome.extension.getURL("iframe.html")
document.body.appendChild(iframe);
function toggle(){
if(iframe.style.width == "0px") {
iframe.style.width="400px";
} else {
iframe.style.width="0px";
}
}
chrome.runtime.onMessage.addListener(function(msg, sender){
if(msg == "toggle"){
toggle();
}
if(msg == "author") {
prevDOM.classList.add(MOUSE_MARKED_CLASSNAME);
chrome.runtime.sendMessage({command:"append-author",el:prevDOM.innerHTML,selector:$(prevDOM).getPath()}, function(response) {});
}
})
// find unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
marker.css
.crx_mouse_visited {
background-clip: #bcd5eb!important;
outline: 1px dashed #e9af6e!important;
z-index : 0!important;
}
.crx_mouse_marked {
background-clip: #bcd5eb!important;
outline: 5px solid #e9af6e!important;
z-index : 0!important;
}

Adding nodes to bootstrap treeview dynamically

I currently have a huge JSON file (over 15k lines, size might increase) with which I want to construct a bootstrap-treeview. Adding all the nodes would make loading of page really slow, so I plan to create a service to fetch JSON of selected nodes and populate the treeview accordingly. This is what I have right now.
<!DOCTYPE html>
<html>
<head>
<title>Bootstrap Tree View</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="./TreeView_files/bootstrap-treeview.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Bootstrap Tree View - DOM Tree</h1>
<br/>
<div class="row">
<div class="col-sm-12">
<label for="treeview"></label>
<div id="treeview"/>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="./TreeView_files/bootstrap-treeview.js"></script>
<script type="text/javascript">
function buildDomTree() {
var tree = [
{
text: "Parent 1",
nodes: [
{
text: "Child 1",
nodes: [
{
text: "Grandchild 1"
},
{
text: "Grandchild 2"
}
]
},
{
text: "Child 2"
}
]
},
{
text: "Parent 2"
},
{
text: "Parent 3"
},
{
text: "Parent 4"
},
{
text: "Parent 5"
}
];
return tree;
}
$(function() {
var options = {
bootstrap2: false,
showTags: true,
levels: 5,
data: buildDomTree()
};
$('#treeview').treeview(options);
});
</script>
</body>
Treeview can go 4 levels deep, and I want to fetch next level JSON each time a node is clicked. So, if I click on "Parent 5", it should pop out sub nodes.
I have no clue how to add nodes dynamically. Any help would be really appreciated.
Answer on request: I have found a way to do it, although, it is a tad inefficient. What I've done is, I keep a state of all expanded nodes, and when I click on a node to expand it, I make an HTTP request, add the new nodes to the old one, redraw the entire tree and re-expand all the previously expanded nodes. I know this is inefficient, but it is the only way I could find without going into the nitty-gritty and essentially recreating the entire tree myself (which is just a glorified recursion application).
Here's the code I ran with when I posted the question. There is obviously room for improvement.
var expandedNodes = [];
var tree = [];
$(function()
{
$.post("http://localhost:8000/getLevel1", function( data )
{
var JSObject = JSON.parse(data);
for (j in JSObject)
tree.push(JSObject[j]);
createTree();
});
});
function createTree(){
var options = {
bootstrap2: false,
showTags: true,
levels: 0,
data: tree,
expandIcon: 'fa fa-chevron-right',
collapseIcon: 'fa fa-chevron-down',
onNodeExpanded: nodeExpand,
onNodeCollapsed: nodeCollapse,
onNodeSelected: nodeSelect,
onNodeUnselected: nodeUnselect
}
$('#treeview').treeview(options);
for (node in expandedNodes)
$('#treeview').treeview('expandNode', [ expandedNodes[node], { levels: 0, silent: true } ]);
$('#treeview').treeview('expandNode', 0, { silent: true } );
};
function nodeExpand(event, data)
{
expandedNodes.push(data.nodeId);
var requestObject = []
requestObject.push(data.text);
var parent, dummy = data;
while ((parent = $('#treeview').treeview('getParent', dummy.nodeId))["nodeId"] != undefined)
{
requestObject.push(parent.text);
dummy = parent;
}
$.post("http://localhost:8000/getNode?param=" + JSON.stringify(requestObject), function(retVal)
{
var JSObject = JSON.parse(retVal);
var node = findNode(requestObject);
node.nodes = JSObject;
createTree();
});
}
function nodeCollapse(event, data)
{
var index = expandedNodes.indexOf(data.nodeId);
if (index > -1)
expandedNodes.splice(index, 1);
}
function nodeSelect(event, data)
{
if (data.state.expanded == true)
$('#treeview').treeview('collapseNode', data.nodeId);
else
$('#treeview').treeview('expandNode', data.nodeId);
//$('#treeview').treeview('unselectNode', [ data.nodeId, { silent: true } ]);
}
function nodeUnselect(event, data)
{
}
function findNode(array)
{
var searchIn = tree; //array
var lastFound = tree;
for (var i = array.length - 1; i >= 0; i--)
{
var obj = searchInObject(searchIn, array[i]);
searchIn = obj.nodes;
lastFound = obj;
}
return lastFound;
}
function searchInObject(objectArray, string)
{
for (var index in objectArray)
if (objectArray[index].text == string)
return objectArray[index];
}
$(document).ready(function () {
var trigger = $('.hamburger'),
overlay = $('.overlay'),
isClosed = false;
hamburger_cross();
$('#wrapper').toggleClass('toggled');
trigger.click(function () {
hamburger_cross();
});
function hamburger_cross() {
if (isClosed == true) {
overlay.hide();
trigger.removeClass('is-open');
trigger.addClass('is-closed');
isClosed = false;
$('#open_arrow').removeClass('fa-chevron-circle-left').addClass('fa-chevron-circle-right');
} else {
overlay.show();
trigger.removeClass('is-closed');
trigger.addClass('is-open');
isClosed = true;
$('#open_arrow').removeClass('fa-chevron-circle-right').addClass('fa-chevron-circle-left');
}
}
$('[data-toggle="offcanvas"]').click(function () {
$('#wrapper').toggleClass('toggled');
});
});
Point of interest would be nodeExpand and createTree methods.

Angular bind-html + $sce still remove the inline style

I write simple custom filter that return string value.
The value contain html string and inline style
When binding angular remove the styles
Here is my filter
siteApp.filter('specialText', ['$filter', '$sce', function ($filter, $sce) {
return function (code, items, defaults) {
var out = defaults;
if (items && items.length) {
var arr = $filter('filter')(items, { code: code }, true);
if (arr && arr.length > 0) {
out = arr[0].value;
$sce.trustAsHtml(out);
}
}
return out;
};
}]);
And This is my html
<div ng-bind-html="'body_message' | specialText : specific_texts :''"></div>
The original text contain inline style but angular remove inline style on binding
How can I keep the inline styles
FULL EXAMPLE CODE
<body >
<div ng-app="siteApp">
<div ng-controller="ctrl">
{{'test1' | specialText : arr :'missing....' }}
<div ng-bind-html="'test1' | specialText : arr :'missing....'"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-sanitize/1.5.6/angular-sanitize.js"></script>
<script>
var siteApp = angular.module('siteApp', ['ngSanitize']);
siteApp.filter('specialText', ['$filter', '$sce', function ($filter, $sce) {
return function (code, items, defaults) {
var out = defaults;
if (items && items.length) {
var arr = $filter('filter')(items, { code: code }, true);
if (arr && arr.length > 0) {
out = arr[0].value;
$sce.trustAsHtml(out);
}
}
return out;
};
}]);
siteApp.controller('ctrl', ['$scope', function ($scope) {
$scope.arr = [{ "code": "test1", "rte": true, "value": "<p style=\"text-align: left;\">First Row</p>\n<h1 style=\"text-align: center;\">Second Center Row</h1>" }];
}]);
</script>
</body>
You are not using the output of $sce.trustAsHtml(out). Inside your if statement try out = $sce.trustAsHtml(out); and it should be ok.

How do I register the on-tap in a paper-icon-button in an ag-grid cell renderer?

I am working on adding ajax call to paper-icon-buttons that are rendered in an ag-grid cell renderer. Here is the script in my custom polymer component. The paper-icon-buttons do show up and clicking on them causes the ripple, but the functions in the on-tap are not being called.
Is there a better way to add the paper-icon-button entries to the cell? How can I add the registration of the on-tap properly?
Thank you!
<script>
function sourceRenderer(params) {
if (params.value)
return '<span>' + params.value + ''
else
return null;
}
function innerCellRendererA(params) {
var imageFullUrl = '/images/' + params.data.type + '.png';
if (params.data.type == 'entity') {
var entityUrl = '/analyze/' + params.data.asource + '/' + params.data.amodel + '/' + params.data.sourceName;
return '<img src="'+imageFullUrl+'" style="padding-left: 4px;" /> ' + params.data.name + ' (' + params.data.sourceName + ')';
}
else if (params.data.type == 'model') {
var entityUrl = '/harvest/' + params.data.asource + '/' + params.data.name;
return '<img src="'+imageFullUrl+'" style="padding-left: 4px;" /> ' + params.data.name + '';
}
else
return '<paper-icon-button src="'+imageFullUrl+'" on-tap="testjdbc" data-args="'+params.data.classname+'~~'+params.data.url+'~~'+params.data.username+'~~'+params.data.password+'"></paper-icon-button> ' +
'<paper-icon-button src="/images/database_export.svg" on-tap="harvestmodel" data-args="'+params.data.classname+'~~'+params.data.url+'~~'+params.data.username+'~~'+params.data.password+'"></paper-icon-button> ' + params.data.name;
}
Polymer({
is: 'easymetahub-analyze',
properties: {
sourcelist: {
type: Array,
notify: true
}
},
testjdbc: function(e){
alert('Foo');
var args = e.target.getAttribute('data-args').split('~~');
},
harvestmodel: function(e){
alert('Bar');
var args = e.target.getAttribute('data-args').split('~~');
},
handleData: function(e) {
var resp = e.detail.response;
this.sourcelist = resp;
},
ready: function() {
},
attached: function() {
agGrid.initialiseAgGridWithWebComponents();
var columnDefs = [
{
headerName: "Name",
'field': 'name',
width: 350,
cellRenderer: 'group',
sort: "asc",
cellRendererParams: {
innerRenderer: innerCellRendererA
}
},
{headerName: "Database Type", field: "databasetype", width: 120 },
{headerName: "URL", width: 250, field: "url" },
{headerName: "User Name", field: "username", width: 120 }
];
var gridOptions = {
columnDefs: columnDefs,
enableColResize: true,
rowHeight: 36,
enableSorting: true,
getNodeChildDetails: function(file) {
if (file.children) {
return {
group: true,
children: file.children,
expanded: file.open,
field: 'name',
key: file.name
};
} else {
return null;
}
},
onGridReady: function(params) {
params.api.sizeColumnsToFit();
}
};
this.$.myGrid.setGridOptions(gridOptions);
var eInput = this.$.quickFilterInput;
eInput.addEventListener("input", function () {
var text = eInput.value;
gridOptions.api.setQuickFilter(text);
});
},
detached: function() {
this.$.myGrid.api.destroy();
}
});
</script>
agGrid's grid options has a property for a callback -- onModelUpdated -- that is called when new rows are added to the grid.
attached: function() {
var self = this;
var gridOptions = {
...
onModelUpdated: function(e) {
self._bindGridIconTap();
}
};
}
You could use this event to query the grid for its paper-icon-buttons and add their on-tap attributes as event handlers.
_bindGridIconTap: function() {
this._bindActionsOnGrid('paper-icon-button', 'tap');
},
_bindActionsOnGrid: function(selector, eventName) {
var self = this;
var buttons = this.$.myGrid.querySelectorAll(selector);
buttons.forEach(function(b) {
self._bindEvent(b, eventName);
});
},
_bindEvent: function(el, eventName) {
var self = this;
var methodName = el.getAttribute('on-' + eventName);
var method = self[methodName];
if (method) {
el.addEventListener(eventName, function(e) {
method(e);
e.stopPropagation();
e.preventDefault();
return false;
});
} else {
console.warn(el.localName, 'listener method not found:', methodName);
}
}
plunker
Note you have a bug in:
var args = e.target.getAttribute('data-args').split('~~');
In a tap event for paper-icon-button, e.target is the icon image. You actually want e.currentTarget, which I've done for you in the Plunker.

Prototype Remove HTML?

I have the following HTML
<div id="top-right">
<span id="top-right-name">sometexthere</span> | link
</div>
And the following prototype JS
Event.observe(window, 'load', function() {
try {
if ($$('#top-right')!=null) {
var topmenu = document.getElementById('top-right');
var value = topmenu.innerHTML;
// define an array of replacements
var replacements = [
{ from: '|', to: ' ' },
{ from: '|', to: ' ' },
{ from: '|', to: ' ' },
{ from: '|', to: ' ' }
];
for (var i = 0, replacement; i < replacements.length, replacement = replacements[i]; i++) {
// replace
value = value.replace(replacement.from, replacement.to);
}
// replace the old text with the new
topmenu.innerHTML = value;
}
}
catch(ex) {
}
});
I am trying to remove the " | " after the </span> automatically onload of the this JS - but I just cant seem to do it .
Can someone assist ?
Thanks
It seems to be a syntax error somewhere, perhaps in your loading of Prototype. The below snippet worked fine for me :)
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>
<div id="top-right">
<span id="top-right-name">sometexthere</span> | link
</div>
<script type="text/javascript">
Event.observe(window, 'load', function() {
try {
if ($$('#top-right')!=null) {
var topmenu = document.getElementById('top-right');
var value = topmenu.innerHTML;
// define an array of replacements
var replacements = [
{ from: '|', to: ' ' }
];
for (var i = 0, replacement; i < replacements.length, replacement = replacements[i]; i++) {
// replace
value = value.replace(replacement.from, replacement.to);
}
// replace the old text with the new
topmenu.innerHTML = value;
}
}
catch(ex) {
}
});
</script>
Edited to reflect problem