How can I display remote users cursor and selection in Quill - collaboration

I've been working with Quill for a short time and have been focused on getting collaborative editing working. So far it's going well and I have a fully working collaborative editor!
I want to show the selection and cursor position of other users, but I can't think how to properly approach this problem with Quill.
I essentially want to add markup to the rendered document, without adding any content to the actual document model. Is this possible? Where should I start?

You need to use "quill-cursors" package and then listen to selection-change event:
editor.on("selection-change", function (range, oldRange, source) {
console.log("Local cursor change: ", range);
});
Then broadcast this data to other remote users, and then render the remote cursor:
const cursors = editor.getModule("cursors");
cursors.createCursor(id, user.name, userColor);
cursors.moveCursor(id, cursorRange); // <== cursor data from previous step
cursors.toggleFlag(id, true);

In Quill 0.20, there was an example with multiple cursors working. The approach was a sibling absolutely positioned <div> that contained the cursors and synchronized with selection-change information from the editor. To not delay the 1.0 release this demo and feature was not updated with the new API but support is planned. You can try a similar approach in the meantime and of course the code is still available. You can also track the feature on Github Issues.

Related

Change not-allowed cursor on drag in React

When I drag an image in react that has draggable set to true, I get not-allowed / no-drop cursor. I can't figure out how to target it with CSS to overwrite. The way I handle the drag is onDragStart then onDragEnd.
Without some reproducable code i just can try a shot in the dark: You have to call at the beginning of your drag handler e.preventDefault();.
I faced the same issue back when I was working on one of my React projects, so I did some research and tested several methods that I found, but nothing worked for me. You see there is a property named DataTransfer.effectAllowed that specifies the effect that is allowed for drag operation, and there is a limitation to this API as you can read here -
https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/effectAllowed
Changing the styles or attaching an eventListener won't work, you have to use a different backend for this. Use react-dnd and react-dnd-touch-backend or react-dnd-html5-backend npm packages as a custom backend.
https://www.npmjs.com/package/react-dnd
https://www.npmjs.com/package/react-dnd-touch-backend
https://www.npmjs.com/package/react-dnd-html5-backend

Unable to get current click value through Adobe Launch

I created a click event in adobe launch which will capture the value of the link and send it to analytics. I have created a data element for saving the value and in my DOM I am saving value in local storage.
Local storage code:
$('.card').click(function() {
var name = $(this).text();
localStorage.setItem('productName', name);
});
My problem is when I click the first link no value got saved, but after when I click second link it saves the value of first link and on third link saves value of second link and so on. I want to save current link value in evar3 variable.
Data element:
Rule:
Set variables:
Thanks,
Harshit
I'm scratching my head a little bit about why your jQuery selector doesn't match your Rule selector, but that's probably not immediately related or relevant, considering you said you are seeing data pop, in general, so at face value, I'm going to ignore that.
But in general, it sounds like your jQuery code is getting executed after the Rule is evaluated, so it's effectively one step behind. I'm not sure there's much if anything you can do about that if you aim to keep two separate click event listeners like this.
You're going to have to restructure to have one chain off the other (which I don't think is actually feasible with jQuery > Launch as-is. Maybe if you write two separate custom code blocks with promise chaining but that kind of sidesteps Launch and IMO is over-complicating things to begin with (see below)). Better yet, merge them into a single click event listener. On that note..
Why do you have two separate click event listeners? Is the sole reason for this to pass off the link text? Because you can reference the clicked element in the Launch Rule itself.
You should be able to reference %this.innerText% in the Set Variables fields. Or you can reference this object in custom code boxes within the Rule.
IOW at face value I don't see why you need or should be creating/using that jQuery listener or pushing to local storage like that.
Update:
You commented the following:
I tried with %this.innerText% it is also not showing current values. I
am pushing the value first to local storage because my link values are
generating on runtime through an API. They are not hardcoded. I am
also trying to figure out why my rule is getting fired before my
jquery is evaluated. But when I check in console using
_satellite.getVar('Product Name'); it shows me the correct value, but in debugger console value is wrong. Can you show me the way you want
to create rule to getting it fired correctly ? Thanks,
Okay so your link values are generated runtime through an API call? Well okay now that sounds like where the (timing) issue is, and it's the same issue in principle I mentioned you prolly had between the jQuery and Launch code. Assuming you can't change this dynamic link functionality, you have two options:
1. Explicitly trigger a launch rule (direct call rule) in a callback from the API code that's generating the link values.
This is the better method, because you won't have race condition issues with your API vs. link tracking code. The biggest caveat about this method though is that it requires requires you to actively add code to the site, which may or may not be feasible for you.
I have no idea what your code for generating the link looks like, but presumably it's some ajax call and generated from the success callback. So in the callback, you'd add a line of code something like this:
_satellite.track('product_image_click', {
text : elem.innerText
});
Again, I don't know what your API code that generates the link looks like, but presumably you have within it some element object you append to or update the DOM, so for this example, I will refer to that as elem.
'product_image_click' - This is the value you use for the direct call rule identifier in the interface, e.g. :
And then _satellite.track() call also includes an object payload in 2nd argument that you can pass to the direct call rule. So in the code above, I set a property named text and give it a value of elem.innerText.
Then, within the direct call rule, where you set variables, the data you passed can be referenced from event.details object within custom code box (e.g. event.details.text), or using % syntax within the interface fields (e.g. %event.details.text%).
2. Make use of setTimeout to delay evaluating the link click.
The one advantage of this method over option #1 is that it is passive. You don't have to add code directly to your site to make it work.
However, this is the shadier option, because you don't really know how long it will take for your link to be generated. But generally speaking, if you can determine it takes on average say 250ms for the link generation API to do its thing, and you set the timeout to be called at say 300-500ms, then you will probably be okay most of the time. However, it's never a 100% guarantee.
In addition, if clicking on the link ultimately redirects the visitor to another page, then this solution will likely not work for you at all, since the browser will almost certainly have navigated before this has a chance to execute. Because of this, I normally I wouldn't even mention this as an option, but since you mentioned this link involves an API that generates the link values when clicked, I figured maybe this isn't a navigation / redirect link, so maybe this is an option you can go for, if you can't do option #1.
First, create a direct call rule the same as shown in option #1. This will be the rule that receives the link text and makes the Adobe Analytics (or whatever other marketing tag) calls.
Then, create a separate rule as a click event, similar to what you are currently trying to do (listening for the link click, based on your css selector). In this rule, you won't be setting any AA variables. Instead, add a custom js box with code like this:
var elem = this;
(function (elem) {
window.setTimeout(function() {
_satellite.track('product_image_click', {
text : elem.innerText
});
}, 500);
})(elem);
So when the rule is triggered, it will create a setTimeout callback to call the first rule, passing the link text in the payload. And.. hopefully the 500ms timeout shown in the code example is enough time for the API to do its thing. You may be able to adjust it up or down.
Rather than defining it in Data Element I would say its better to keep it directly in the Rule itself. Please try with this %this.#text%.
Let me know if this helped.

Adobe Scene7 BasicZoomViewer: How to reset zoom

Question
I'm working with Adobe Scene7 BasicZoomViewer and I'm looking for a way to tell the ZoomViewer to reset the zoom so that the user is no longer zoomed in on an image but instead will show the default "zoom" level.
What I've found
The closest thing I found to what I need is this reset property ZoomView.reset which "Resets the viewport when the frame (image) changes. If set to 0 it preserves the current viewport with the best possible fit while preserving the aspect ratio of the newly set image".
This looks close to something I need but it states that it will reset or preserve the aspect ratio when a new image has been inserted but I am not inserting new images.
Demo from Adobe
There is a button on the image that the API inserts into the page that resets the zoom level. Adobe provides a demo page that shows what I'm working with. If you look at the bottom left, the right-most button is the reset button. When clicked, it has to make some kind of API call and I need to figure out which one it is.
Edit
I've been able to find a minified version of the BasicZoomViewer and I am currently attempting to make sense of the code.
There is an event listener placed on the "Zoom Reset Button" that just simply calls a reset() method on line 274 in the uglified version of the file. Currently, I am trying to make sense of the file and figure out how to access this method.
c.zoomResetButton.addEventListener("click", function () {
c.zoomView.zoomReset()
});
I will be answering my own question. If someone finds a better way please feel free to answer as well.
tldr;
Create a variable to hold the instance of your s7viewers.BasicZoomViewer() and inside of that you can access the event handlers and much more.
Example of calling the reset zoom handler
// instantiate the s7viewers class and save it in a variable
var s7BasicZoomViewer = new s7viewers.BasicZoomViewer({
containerId: 's7viewer',
params: {
asset: assetUrl,
serverurl: serverUrl
})
// example of how to call the "zoomReset()" method
s7BasicZoomViewer.zoomResetButton.component.events.click[0].handler()
Explanation
After digging through the minified code that was uglified I found an event listener on the s7zoomresetbutton DOM class name, or perhaps it's watching for the ID of that DOM element which is the same ID as the container div for your S7 BasicZoom Viewer plus some added text to make this ID unique. For example, if the container div is s7viewer then the reset zoom button will have an ID of s7viewer_zoomresetbutton.
Now, going through the code I found this event listener which let me know there must be some way to call the zoomReset() method.
c.zoomResetButton.addEventListener("click", function () {
c.zoomView.zoomReset()
});
In the code above, the value of c is this or in other words it's the instance of your S7 BasicViewerZoom and in my case I have multiple depending on how many images I need to zoom on.
When instantiating the s7viewers class you can then reference that instance later and access the event handlers on each button and other properties and methods.
From there it was just looking through the object returned from the instance and calling the handler for the reset button.

How to change css style locally and save this changes

i have an webapp and i need to onclick change css style, but i need to save this change in the webapp instaled into the phone of my user, and only in his phone. The javascript for onclick change css style it's working but i don't have no idea to how to save this css changes.
Can somebody help me with this?
Since now thanks
In general CSS styles can't be directly saved on an HTML client.
What you can do is make an Ajax call back to your server and save the information there. The next time the user asks for the page send HTML with the appropriate style class already on the element you wish to style based on the saved information.
There are several hackish client side possibilities involving JavaScript & cookies or local storage but I would avoid that sort of solution if at all possible since it's very likely to lead to an annoying flicker as the page loads and renders styled one way and then the JavaScript finally runs and corrects the style.
To elaborate on my comment:
el1.addEventListener('click', function() {
el2.style.color = 'red';
localStorage['color'] = el2.style.color;
})
And then on startup:
window.addEventListener('load', function() {
if (localStorage['color']) {
el2.style.color = localStorage['color'];
}
}
Of course, you may want to add error checking and fallbacks as appropriate.

Turn off notifications for Meteor collections

How do I tell Meteor to stop publishing changes on a collection (for a moment)?
Also how to tell it to resume and that the collection changed?
Basically (on the server):
People = new Meteor.Collection("people")
insertPeople = ->
// Don't notify clients of the following changes
// Insert a bunch of people into the People collection
// Resume notifications
Put a flag in each document, 'updating'.
Add the new ones with this set to true; render their template with a css class that hides them based on this field.
When ready, update the collection to updating: false. They will be visible pretty quickly.
This being said, there are events you can plug into to make transitions more pleasant/animated. Didn't think you were asking that, but it may be a better answer.
To the comment:
Inserting a template for an additional document triggers DOM changes, which are fairly expensive, and then the device has to figure out how to display. Updating a property requires just the second part, that the device has to figure out how to display.

Resources