The OpenERP web interface relies heavily on javascript, QWeb, jQuery (I think), and css.
The default view has a black menu bar along the top, a side menu bar along the left, and the rest of the screen for the content being served.
The issue I was trying to "fix" is that it is displayed as one large page, meaning if you scroll the page the top menu and side menu also scroll off.
I have a nearly working solution:
adjust top menu bar to be fixed
adjust side menu bar to be fixed
adjust remainder to take up the remaining space
In order to do that I also had to add a new css class, which I called oe_main_window, and located the code which sets up the view to add oe_main_window to the div with the class oe_view_manager_current.
This works fine for primary views.
However, if I click on a link in a primary view, say to show a product, it removes the oe_main_window class and messes up the display. If I go into developer tools and add oe_main_window back the display is again correct.
I have tried searching for where this is happening, but so far have failed to locate the appropriate code either to not remove the new class or to add it back after the transition.
Any ideas?
You can find how to put a breakpoint on DOM mutation here:
https://developer.chrome.com/devtools/docs/javascript-debugging#breakpoints-mutation-events
mainly it's opening the chrome dev-tools using F12, right click on your div and click "Break on...>> Attributes modifications"
The final solution to the problem involved targeting the first oe_view_manager found. Here's the (clumsy) JavaScript:
function addOeMainWindow() {
var newChildren = [];
var currentChild, getAttr, oldClass, newClass;
var i;
for (i=0; i<document.childNodes.length; i+=1) {
newChildren.push(document.childNodes[i]);
}
while (newChildren.length > 0) {
currentChild = newChildren.shift();
getAttr = currentChild.getAttribute;
if (getAttr !== undefined) {
oldClass = currentChild.getAttribute("class");
if (/oe_view_manager/.test(oldClass) && !/oe_main_window/.test(oldClass)) {
newClass = oldClass + " oe_main_window";
currentChild.setAttribute("class", newClass);
return;
}
}
for (i=0; i<currentChild.childNodes.length; i+=1) {
newChildren.push(currentChild.childNodes[i]);
}
}
return;
}
Related
As an alternative to the title attribute for smartphone devices, I would like to use a button that would show a tooltip for each title attribute attached to a visible element of class icon. They can be placed anywhere on screen depending on the page, even using absolute positioning.
I tried to do that using Bootstrap tooltips, which can easily be done:
let icons = document.getElementsByClassName("icon");
for (let iIcon = 0; iIcon < icons.length; iIcon++){
if (icons[iIcon].offsetParent !== null){
let tooltip = new bootstrap.Tooltip(icons[iIcon], { boundary: document.body });
tooltip.show();
}
}
However, there is no mechanism to prevent collision and the tooltips appear superimposed.
Can collisions be avoided in an automated manner, either using Booststrap tooltips or other module?
I'm writing an Chrome Extension for Google Calendar, which adds an div under the header section of Google Calendar.
For simplicity, this is what essentially happens in Chrome Extension content script, you can just paste this code in the console of calendar.google.com to check what happens:
let header = document.querySelector("header[role='banner']");
let appContainer = document.createElement('DIV');
appContainer.style.height = '200px';
appContainer.style.backgroundColor = 'red';
header.parentNode.insertBefore(appContainer, header.nextSibling);
The problem I experience is, after creating or canceling the creation of an event in calendar view, the window scrolls up and doesn't show the page header now.
Anyone have an idea how to keep page steady after creating or canceling of an event in calendar view, and keep the div I append via Chrome Extension content script too?
EDIT: Here are screenshots of how it looks like before the event creation/cancel and after:
Normally, without your extension, header.parentElement contains two elements that count towards header.parentElement.clientHeight: header, and the div that contains the calendar itself:
(It also contains a few floating buttons and hidden divs and stuff, but those aren't important here.)
These two elements, header and calendar, have heights that are calculated specifically so that header.clientHeight + calendar.clientHeight is equal to the height of the page. Under normal circumstances this is perfect since it means there's no need for scrolling.
However, in your case, that you add an extra div, which pushes calendar down:
Normally you would be able to scroll down yourself to see the bottom of calendar, but since the scroll bar is disabled, you can't. However, when you create an event, your browser sees that you are trying to access the bottom of calendar, so it scrolls down automatically to show you the bottom of calendar. Since the whole page is now scrolled down to make the bottom of the page visible, the top of the page is now invisible, resulting in the behavior that you describe.
The way to fix this is to make adjust the height of calendar so that header.clientHeight + appContainer.clientHeight + calendar.clientHeight is equal to the height of the page, rather than just header.clientHeight + calendar.clientHeight. This can be done by adding the following code:
//Getting calendar can be a bit tricky since it's just one dive among others.
//While it does have a CSS class, I don't know how that name is determined or if it will change in future versions of Google Calendar, so it's best not to use it.
//What I do here instead is that I select the first child of header.parentElement that has parts outside of the page frame
const getCalendar = () => [...header.parentElement.querySelectorAll(":scope > div")].find(d => {
let boundingRect = d.getBoundingClientRect();
return boundingRect.y + boundingRect.height > document.body.clientHeight;
});
let calendar = getCalendar();
//This function adjusts the height of calendar
const adjustHeight = () => {
calendar = calendar || getCalendar(); //calendar may be null in the beginning before all the elements were set, so if it's null try to select it again
if(calendar != null){
calendar.style.height = (calendar.parentElement.clientHeight - header.clientHeight - appContainer.clientHeight) + "px"; //Adjust the height of calendar
}
};
window.addEventListener("resize", adjustHeight); //Adjust the height whenever the window gets resized
window.addEventListener("load", adjustHeight); //Adjust the height on page load
I have the following drop-down :
<select>
<option></option>
<option>Closed</option>
<option>Open</option>
</select>
with the associated style:
select {
font-family: Cursive;
width:200px;
position: relative;
z-index: 100;
padding-right: 25px;
}
My problem is that the drop-down is moving upward on IE 11:
Where as on chrome it is working fine.
Any idea ?
Like mentioned in the comments, select menus are very browser specific and hard to style. Some even send the options into the twilight zone where they are seemingly not even a part of the window and any events will return null. It might not be worth trying to get this to look the same across browsers, also because of the mobile implementations, but I happened to be making something like this for no apparent reason. As it coincides with your question I might as well post it.
It's not the prettiest thing when it comes to HTML and CSS because it requires four additional elements - one wrapper (commonly used for styling select boxes with overflow hidden but I took a slightly different approach because I thought it looked better) and three absolutely placed elements. One is a styled button, another will hide the scrollbar that appears and the third is a minor hack.
Most important thing is that the user will not be able to click the select menu itself. When this happens, most is lost because after that it's limbo. For that the third element will be used. It will be put on top of the select box. Then when it's clicked, instead of directly opening the menu it will be faked by changing the size of the select element. The div covering the right side also serves another purpose. It's initially placed at the bottom and by getting it's offset we'll know the height of the box. This will be used to resize the button and set the correct size for the overlaying div.
Looks to be behaving quite predicatbly on all major Windows desktop browsers. For the mobile implications this script uses a touch support feature test and reverts to normal layout if that is the case. Could probably be tweaked (with a screen size check) to not exclude trackpad users.
Demo
Not too much relevant CSS. But important to style body background the same as the bar on the right. Transparency is used so the actual menu isn't visible before the image for the button loads.
$(function() {
var hub = $('#box'), list = $('select'),
impel = $('#open'), commit = $('#mark'), tract = $('#refer'),
zenith = tract.position().top,
extent = list.children().length, active;
if (touch()) {
impel.add(commit).add(tract).remove();
hub.fadeTo(0,1);
return;
}
impel.add(commit).height(zenith);
tract.addClass('bar');
hub.fadeTo(0,1).on('mouseup click', function(e) {
e.stopPropagation();
});
commit.mouseup(function() {
flip();
show();
active = true;
});
list.add(impel).click(function() {
flip();
active = !active;
if (active) show();
else hide();
});
$(window).click(function() {
if (active) {
flip();
hide();
active = false;
}
});
function show() {list.attr('size', extent)}
function hide() {list.removeAttr('size')}
function flip() {commit.toggle()}
function touch() {
return 'ontouchstart' in window
|| navigator.maxTouchPoints > 0
|| navigator.msMaxTouchPoints > 0;
}
});
We would like to have a greater control of where and how we position the tinymce toolbar. We found this option fixed_toolbar_container which solves a lot for us but brings us an anoying problem. The documents say the fixed_toolbar_container (http://www.tinymce.com/wiki.php/Configuration:fixed_toolbar_container) can be used to have a fixed toolbar. But we actually would like to use it to be absolute so we can position it relative to it's container.
I created a JS Fiddle to demonstrate the problem: http://jsfiddle.net/ronfmLym/2/. When you open the toolbar by clicking on the text the toolbar will be positioned absolute. When you open a submenu (i.e. by clicking on "file") a submenu will open. Now when you start scrolling the submenu won't stick to the toolbar. This is because these submenu's get the mce-fixed class because we set the fixed_toolbar_container property.
<div class="element">
<div class="toolbar-container"></div>
<div class="content">
<p>Text</p>
</div>
</div>
Is there any way to make the submenu's stick to the toolbar when positioned absolute and scrolling? Keep in mind that we are switching to a fixed positioning when the toolbar is going off screen.
We thought we could maybe fix it by modifying the container element of de submenu's by using the piece of code below and overwriting the top-position of the submenu's and setting the positioner to absolute with css. But that seems to mess up the tooltips and tinymce doesn't recalculate the "left" css-property of the submenu's so the position in still off.
tinymce.ui.Control.prototype.getContainerElm = function() {
return document.getElementById('toolbar-container');
};
The only corresponding question I could find on stackoverflow was this one: TinyMCE push down submenus using fixed_toolbar_container, no answers there.
Tried wrapping the toolbar in a div and using position:relative; to try and hack it together, but didn't cooperate this time.
It appears that the toolbar actually is accounting for its position at the time of click. So your only conflict is if the opened toolbar is position:absolute and then changes to position:fixed or vice versa.
Your best [manual] bet would be to call a function at the same time that you change the position of the toolbar that:
Detects if any menus are open.
Changes the toolbar position.
Reopens the menus that were open.
The lazy (discouraged) fix would be to close all submenus whenever the position changes. This will fix the layout, but it will require the user to click once again to bring the menu back.
Sorry this isn't a silver bullet answer :(
This answer follows Brian John's suggestion:
I'm using this method to position any open mce-floatpanel (This is typescript, but it shouldn't be too hard to convert to ES or whatever you need.):
positionTinyMceDropdowns() {
// TODO: You'll need to replace all occurrences
// of this.mceWrapperElement with whatever is
// wrapping your TinyMCE. If you have only a
// single instance, you can just replace it
// with document
const button = <HTMLElement> this.mceWrapperElement.getElementsByClassName('mce-opened').item(0);
const items = document.getElementsByClassName('mce-floatpanel');
let wrapperNode: HTMLElement;
for (let i = 0; i < items.length; i++) {
const currentItem = <HTMLElement> items.item(i);
if (currentItem.style.display !== 'none') {
wrapperNode = currentItem;
break;
}
}
if (!wrapperNode || !button) {
return;
}
const styles = wrapperNode.style;
styles.display = 'block';
styles.position = 'absolute';
const bodyRect = document.body.getBoundingClientRect();
const buttonRect = button.getBoundingClientRect();
// get absolute button position:
let y = buttonRect.top - bodyRect.top;
y += 33; // toolbar line height;
styles.top = `${Math.floor(y)}px`;
}
The instances I found in which it needs to be called:
on window scroll (or if the editor is wrapped in a scrolling container, then whenever that scrolls)
on window resize (or if the editor is wrapped in a container that resizes without the window being resized, then whenever that container is resized)
So here's a sample for the simplest case in angular (again, adept to whichever js framework you're using):
import { HostListener } from '#angular/core';
// ...
#HostListener('window:resize', ['$event'])
#HostListener('window:scroll', ['$event'])
public onResize() {
this.positionTinyMceDropdowns();
}
Interestingly on iOS devices (and perhaps other mobile devices?) mce-floatpanel wasn't even positioned correctly after it had just been opened. So I had to add this:
tinymceConfig.setup = (editor: TinyMceEditor) => {
editor.on('init', () => {
const panel = this.mceWrapperElement.querySelector('.mce-tinymce.mce-panel');
if (panel) {
panel.addEventListener('touchend', () => {
this.positionTinyMceDropdowns();
});
}
});
};
I think the config setting fixed_toolbar_container is poorly explained in TinyMCE 6 documentation but when you correctly configure it, you'll find it will work much more nice (especially for inline mode) than the default configuration that tries to emulate position:sticky.
In practice, you want to fixed_toolbar_container set to a string that's CSS selector for the container, typically something like "#mycontainer".
After that, you can move the container element using its CSS properties, the TinyMCE user interface nicely follows around. (Modulo typical TinyMCE bugs, of course. For example, the submenus overflow to right with very narrow viewports.)
Note that TinyMCE positions stuff where it uses position:absolute relative to fixed_toolbar_container container and if you move that container around, in some cases you must execute editor.dispatch("ResizeWindow") to trigger TinyMCE to re-calculate the absolutely positioned elements.
See demo using custom container with position:sticky at https://jsfiddle.net/8bndv26t/1/.
An example, http://www.laravel.com
I want to mimic this effect. I've seen it used a lot across the web lately, but I've never seen a tutorial covering how to create it.
Anyone happen to have some instructions or perhaps a tutorial on recreating this effect?
It's called parallax scrolling. You can do such things with skrollr.js.
And a great tutorial
There are plenty of examples and tutorials for sticky headers,
First link on google search for sticky header http://codepen.io/senff/pen/ayGvD
// Create a clone of the menu, right next to original.
$('.menu').addClass('original').clone().insertAfter('.menu').addClass('cloned').css('position','fixed').css('top','0').css('margin-top','0').css('z-index','500').removeClass('original').hide();
scrollIntervalID = setInterval(stickIt, 10);
function stickIt() {
var orgElementPos = $('.original').offset();
orgElementTop = orgElementPos.top;
if ($(window).scrollTop() >= (orgElementTop)) {
// scrolled past the original position; now only show the cloned, sticky element.
// Cloned element should always have same left position and width as original element.
orgElement = $('.original');
coordsOrgElement = orgElement.offset();
leftOrgElement = coordsOrgElement.left;
widthOrgElement = orgElement.width();
$('.cloned').css('left',leftOrgElement+'px').css('top',0).css('width',widthOrgElement+'px').show();
$('.original').css('visibility','hidden');
} else {
// not scrolled past the menu; only show the original menu.
$('.cloned').hide();
$('.original').css('visibility','visible');
}
}