Using MutationObservers in addons - javascript

I am trying to use a MutationObserver inside my addon. Therefore I inject a content-script which then sets the observer. This somehow seems to work, also the detected mutations seem not be be serializable to JSON.
But actually I want to use this library for monitoring mutations. Explicitly this one is officially mentioned by Mozilla regarding mutation monitoring in addons. But this doesn't work at all.
So anybody got a working example for a working mutation-observer (better mutation-summary - see link) inside a content-script?
My code looks like this:
var observer = new MutationObserver(function (mutations) {
self.port.emit("domModified", mutations); //I gets received as 'Array [{}]'
mutations.forEach(function (mutation) {
console.log(mutation.type); //printing mutation results in {}, printing mutation.type results in an actual string
console.log(mutation.target);
console.log(mutation.addedNodes.length);
});
});
observer.observe(unsafeWindow.document.body, {
attributes: true,
subtree: true,
characterData: true,
childList: true
});

This somehow seems to work, also the detected mutations seem not be be serializable to JSON.
Mutations are not serializable, especially because they contains nodes. If you need to pass something from the content script, to the main add-on code, you need to be sure they're JSONable values.
So anybody got a working example for a working mutation-observer (better mutation-summary - see link) inside a content-script?
I never used the library you mentioned, but I used mutation observers quite a lot; and they're working quite fine. You can see an example here: https://github.com/ZER0/tweet-to-read It basically adds a button to every tweet in the stream that contains an external URL; and I needed the mutation observer to add the buttons also in future tweets. You can have a look to the implementation here: https://github.com/ZER0/tweet-to-read/blob/master/data/observable.js
Hope it helps.

Related

Where and how does libUV interact with code on node.js

I wondered a question: "where and how does libUV interact with code on node.js". Lately I was investigating streams, also I read source on github.
Well, let's take source of script called as destroy.js. This script is responding for the destruction of streams: stream.destroy(). After that operation:
in function destroy are set states for streams into values:
writable._stateWritable.destroyed = true
readable._stateReadable.destroyed = true
in function _destroy are set states for streams into values:
writable._stateWritable.closed = true
readable._stateReadable.closed = true
in funtion emitCloseNT:
sets value writable._stateWritable.closeEmmited = true
sets value readable._stateReadable.closeEmmited = true
emmits event close
That's all. Where and how does libUV interact with stream.destroy()? Even documentation of node about writable.destroy says:
This is a destructive and immediate way to destroy a stream
But what is it really? I see only the process of setting state for the streams and only it. So, where does libUV actually destroy stream?
I'm not a subject matter expert, but after debugging the following code, I got a rough idea of what happens behind the scenes:
var cnt = 0;
new stream.Readable({
read(size) {
if (++cnt > 10) this.destroy();
this.push(String(cnt));
}
}).pipe(process.stdout);
Upon this.destroy(), the readableState.destroyed is set to true here, and because of this the following this.push("11") returns false here. If readableState.destroyed had been false, it would instead have called addChunk, which would have ensured that reading goes on by emitting a readable event and calling maybeReadMore (see here).
If the readable stream was created by fs.createReadStream, then the _destroy method additionally calls a close method, which closes the file descriptor.

pdftron copy wrong text

I want to use pdftron and all things work perfect but when i copy text from pdf some characters convert to blank square and question mark, any idea?
here is my pdf.
As you can see below:
I wrote this code:
WebViewer({
path: '/assets/plugins/pdftron',
initialDoc: '/practical.pdf',
fullAPI: true,
disableLogs: true
}, document.getElementById('pdf')).then((instance) => {
// PDFNet is only available with full API enabled
const { PDFNet, docViewer } = instance;
let Feature = instance.Feature;
instance.disableFeatures([Feature.NotesPanel]);
docViewer.on('documentLoaded', () => {
// call methods relating to the loaded document
});
instance.textPopup.add({
type: 'actionButton',
img: '/language.svg',
onClick: () => {
const quads = docViewer.getSelectedTextQuads(docViewer.getCurrentPage());
const text = docViewer.getSelectedText();
$("#out-pdf").html(text);
console.log(quads);
},
});
});
Document does seem to cause incorrect extraction. Extraction is not defined by PDF specification so every viewer handles cases little differently. I your case there is a probably a malformed or incomplete font or unicode map included in the document. We've added multiple fixes to our core components and with those fixes extraction happens correctly. Unfortunately current release of WebViewer does not include these fixes yet. We cannot give exact time schedule when fixes will be land to the WebViewer, but should be at least part of our next major release. For now I would try to see if you can recreate the document and see if that helps. Most of the documents we see and test have no problem with extraction.
Could you create ticket through our support https://www.pdftron.com/form/request/ and attach the document that this happens to the ticket, so I can take a closer look on and get issue resolved faster.

SvelteJS vs ReactJS rendering difference (repaint / reflow)

Here's my naive understanding of how the DOM and browser works
Whenever something in the DOM ( the real dom ) changes the browser repaints or reflows that the DOM. So in simpler terms every time the DOM changes browser needs to recalculate the CSS, do a layout and repaint the web page. This is what takes time in real dom.
So React comes with this virtual DOM and what it actually does is it batches the changes and call applies them on real-dom in one go. Thus, minimizing the re-flow and re-paint.
Then what about Svelte. If it is manipulating the DOM directly how does it controls the repaint/reflow of the browser.
In addition to the (correct) answer above: Svelte "compiles" the code that you provide to it, so the final code can be executed without a library runtime (in contrast to React). And it creates rather readable code, so it is absolutely possible to understand the inner workings.
Note: This will be a bit longer answer - and still leave out many minute details about what is going on under the hood of Svelte. But I hope it helps to demystify some of the aspects what is going on under the hood. Also, this is how Svelte does things as of v3.16.x. Since this is internal, it might change. However, I still find it always rewarding to understand what is really going on.
So, here we go.
First and foremost: The Svelte tutorial has a useful feature to let you see the generated code (right next to the "Result" pane). It might look a bit intimidating at first, but you quickly get the hang of it.
Below code will be based on this example (but further simplified): Svelte tutorial - reactivity/assignments
Our example component definition (i.e. App.svelte) looks like this:
<script>
let count = 0;
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>{count}</button>
Based on this component definition, the Svelte compiler creates a function which will create a "fragment" which receives and interacts with a "context".
function create_fragment(ctx) {
let button;
let t;
let dispose;
return {
c() {
button = element("button");
t = text(/*count*/ ctx[0]);
dispose = listen(button, "click", /*handleClick*/ ctx[1]);
},
m(target, anchor) {
insert(target, button, anchor);
append(button, t);
},
p(ctx, [dirty]) {
if (dirty & /*count*/ 1) set_data(t, /*count*/ ctx[0]);
},
i: noop,
o: noop,
d(detaching) {
if (detaching) detach(button);
dispose();
}
};
}
The fragment is responsible to interact with the DOM and will be passed around with the component instance. In a nutshell, the code inside
"c" will be run on create (creating the DOM elements in memory and also setting up the event handlers)
"m" will run on mount (attaching the elements to the DOM)
"p" will run on update, i.e. when something (including props) changes
"i" / "o" are related to intro/outro (i.e. transitions)
"d" will be run on destroy
Note: The functions like element or set_data are actually very approachable.
For example, the function element is just a wrapper around document.createElement:
function element(name) {
return document.createElement(name);
}
The context (ctx) will hold all instance variables as well as functions. It is nothing more than a simple array. Since Svelte "knows" what each index means at compile time, it can make hard references to the indices at other places.
This code essentially defines the instance context:
function instance($$self, $$props, $$invalidate) {
let count = 0;
function handleClick() {
$$invalidate(0, count += 1);
}
return [count, handleClick];
}
Both the instance method as well as the create_fragment will be called from another function call init. It's a bit more involved, so instead of copy and pasting it here, you can have a look at this link to the source.
The $$invalidate will make sure that the count variable is set as dirty and schedule an update. When the next update runs, it will look at all "dirty" components and update them. How this is happening is really more of an implementation detail. If interested, set a breakpoint in the flush function.
In fact, if you really want to go a bit deeper, I recommend to clone the template app, then create a simple component, have it compiled and then inspect "bundle.js". You can also debug the actual code if you either delete the source maps or deactivate them.
So, for example set the rollup.config.js like so:
output: {
sourcemap: false,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
dev: false,
Note: As shown above, I recommend to also set dev mode to false since this will create more concise code.
One neat feature: Once our app is running, you can also access the app variable (it is assigned to the global window object since it is bundled as an immediately-invoked function expression).
So, you can open your console and simply say
console.dir(app)
which will produce something like this
App
$$:
fragment: {c: ƒ, m: ƒ, p: ƒ, i: ƒ, o: ƒ, …}
ctx: (2) [0, ƒ]
props: {count: 0}
update: ƒ noop()
not_equal: ƒ safe_not_equal(a, b)
bound: {}
on_mount: []
on_destroy: []
before_update: []
after_update: []
context: Map(0) {}
callbacks: {}
dirty: [-1]
__proto__: Object
$set: $$props => {…}
One cool feature is that you can the $set method yourself to update the instance. For example like so:
app.$set({count: 10})
There are also Svelte DevTools which try to make the internals of Svelte more approachable. Somehow, they seemed to affect the render performance of my apps when I personally tried them out, so I don't use them myself. But certainly something to watch.
Well, there you have it. I know this is still rather technical, but I hope it helped to get a better understanding on what compiled Svelte code is doing.
Both libraries minimize how many changes need to be made to the dom. The difference is the way that they figure out what that minimal set of changes is.
React's approach is to have a representation of the dom in memory (the virtual dom). When you set state, it runs the render process again to create another virtual dom. It compares the before and after, finds what changed, and then any changes get pushed to the real dom.
Svelte's approach is that when you set a variable, it sets a flag marking that variable as having changed. It knows which variables are dependent on other variables, so it then steps through any dependent variables and recalculates them, building up a list of what needs to change. Then these changes get pushed to the dom.

MutationObserver in "new" Google Calendar

I have an browser extension for Google Calendar, where I observe the DOM to gather events that are being viewed with:
// content.js
const calendarContainer = document.getElementById('gridcontainer');
const observer = new MutationObserver(mutations => {
// just for test
console.log('content', mutations);
}
observer.observe(calendarContainer, {
childList: true,
characterData: true,
subtree: true
});
It works correctly in "classic" Calendar, but since the last update to Material design, DOM structure is different. So, I tried to change observed element accordingly:
// content.js
const calendarContainer = document.querySelector('div[role="main"]');
Even though I switch between week/day view, schedule or between dates, I cannot receive any new mutations besides first load of the page (after refresh). If you look on the source code trough Developer Tools you can see that DOM is changing, but Observer is somehow not able to see it.
Any thoughts?
According to wOxxOm comment, I have changed it to observe the parentElement, which is not replaced between different calls, and it's working.

Custom Unobtrusive Validation Method Not Firing as Per Documentation

I've been attempting to implement a ASP.NET MVC custom validation method. Tutorials I've used such as codeproject explain that you add data-val-customname to the element. Then jQuery.validate.unobtrusive.js then uses the third segment of the attribute
data-val-<customname>
as the name of the rule, as shown below.
$.validator.addMethod('customname', function(value, element, param) {
//... return true or false
});
However I just can't get the customname method to fire. By playing around I have been able to get the below code to work, but according to all the sources I've read Unobtrusive validation should not work like this.
$.validator.addMethod('data-val-customname', function(value, element, param) {
//... return true or false
});
I've posted an example of both methods
jsfiddle example
Any help would be much appreciated
I've updated my question hopefully to make clearer.
I have finally found got there in the end, but still feels like too much hard work and therefore I've probably got something wrong. Initial I was scuppered by a bug in Chrome Canary 62 which refused to allow the adding of a custom method.
My next issue was having to load jQuery, jQuery.validate and jQuery.validate.unobtrusive in the markup and then isolate javascript implementation in a ES6 class. I didn't want to add my adaptors before $().ready() because of my class structure and loading of the app file independent of jQuery. So I had to force $.validator.unobtrusive.parse(document);.
Despite this I was still having issues and finally debugged the source code and found that an existing validator information that is attached to the form was not merging with the updated parsed rules, and essentially ignoring any new adaptors added.
My final work around and admit feels like I've done too much, was to destroy the initial validation information before my forced re-parse.
Here is the working jsfiddle demo
Here is some simplified code
onJQueryReady() {
let formValidator = $.data(document.querySelector('form'), "validator" );
formValidator.destroy();
$.validator.unobtrusive.adapters.add("telephone", [], function (options) {
options.rules['telephone'] = {};
options.messages['telephone'] = options.message;
});
$.validator.unobtrusive.parse(document);
$.validator.addMethod("telephone", this.handleValidateTelephoneNumber);
}

Categories

Resources