def iommi_debug_panel(part): source_url = source_url_from_part(part) script = r""" window.iommi_start_pick = function() { window.iommi_pick_stack = []; function create(html) { let r = document.createElement('div'); r.innerHTML = html; return r.firstChild; } window.iommi_close_pick_toolbar = function() { window.iommi_pick_stack.forEach(function(el) { el[3].style.backgroundColor = el[2]; }); document.getElementById('iommi-pick-toolbar').remove() }; function update_toolbar() { let toolbar = document.getElementById('iommi-pick-toolbar'); if (!toolbar) { return; } while(toolbar.firstChild) { toolbar.removeChild(toolbar.firstChild); } toolbar.append(create('<div style="float: right" onclick="iommi_close_pick_toolbar()">close</div>')); for (let i in window.iommi_pick_stack) { let x = window.iommi_pick_stack[i]; toolbar.append(create('<div style="background-color: ' + getColor(i) + '">' + x[0] + ' <a href="https://docs.iommi.rocks/en/latest/' + x[1] + '.html">' + x[1] + '</a></div>')); } } let with_iommi_path = document.querySelectorAll('*[data-iommi-path]'); let colors = [ 'rgb(255, 255, 191)', 'rgb(254, 224, 139)', 'rgb(253, 174, 97)', 'rgb(244, 109, 67)', 'rgb(213, 62, 79)', 'rgb(158, 1, 66)', 'rgb(230, 245, 152)', 'rgb(171, 221, 164)', 'rgb(102, 194, 165)', 'rgb( 50, 136, 189)', 'rgb( 94, 79, 162)', ]; function getColor(index) { return colors[Math.min(index, colors.length - 1)] } function mouseenter() { window.iommi_pick_stack.push([this.getAttribute('data-iommi-path'), this.getAttribute('data-iommi-type'), this.style.backgroundColor, this]) this.style.backgroundColor = getColor(window.iommi_pick_stack.length-1); update_toolbar(); } function mouseleave() { if (window.iommi_pick_stack.length) { this.style.backgroundColor = window.iommi_pick_stack.pop()[2]; update_toolbar(); } } function click() { document.querySelectorAll('*[data-iommi-path]').forEach(function (e) { e.removeEventListener('mouseenter', mouseenter) e.removeEventListener('mouseleave', mouseleave) e.removeEventListener('click', click) }); } with_iommi_path.forEach(function (e) { e.addEventListener('mouseenter', mouseenter); e.addEventListener('mouseleave', mouseleave); setTimeout(function(){ e.addEventListener('click', click); }); }); let toolbar = create('<div id="iommi-pick-toolbar" style="position: fixed; left: 0; bottom: 0; width: 100%; background-color: white; color: black; padding: 4px; border-top: 2px solid #1084ff; z-index: 200">'); document.getElementsByTagName('body')[0].append(toolbar); }; """ from iommi.menu import get_debug_menu return get_debug_menu(sub_menu__code__url=source_url).bind( request=part.get_request()).__html__() + mark_safe( f'<script>{script}</script>')
def validate_styles(*, additional_classes: List[Type] = None, default_classes=None, styles=None): """ This function validates all registered styles against all standard classes. If you have more classes you need to have checked against, pass these as the `classes` argument. The `default_classes` parameter can be used to say which classes are checked for valid data. By default this is all the `Part`-derived classes in iommmi. This parameter is primarily used by tests. The `styles` parameter can be used to specify which exact styles to validate. By default it will validate all registered styles. This parameter is primarily used by tests. """ if default_classes is None: from iommi import ( Action, Column, Field, Form, Menu, MenuItem, Query, Table, Filter, ) from iommi.table import Paginator from iommi.menu import ( MenuBase, get_debug_menu, ) from iommi.error import Errors from iommi.action import Actions from iommi.admin import Admin from iommi.fragment import Container from iommi.fragment import Header from iommi.live_edit import LiveEditPage default_classes = [ Action, Actions, Column, get_debug_menu().__class__, Errors, Field, Form, Menu, MenuBase, MenuItem, Paginator, Query, Table, Filter, Admin, Container, Header, LiveEditPage, ] if additional_classes is None: additional_classes = [] classes = default_classes + additional_classes if styles is None: styles = _styles # We can have multiple classes called Field. In fact that's the recommended way to use iommi! classes_by_name = defaultdict(list) for cls in classes: for cls_name in class_names_for(cls): classes_by_name[cls_name].append(cls) # This will functionally merge separate trees of class inheritance. So it produces a list of all shortcuts on all classes called something.Field. shortcuts_available_by_class_name = defaultdict(set) for cls_name, classes in items(classes_by_name): for cls in classes: shortcuts_available_by_class_name[cls_name].update( get_shortcuts_by_name(cls).keys()) invalid_class_names = [] non_existent_shortcut_names = [] for style_name, style in items(styles): for cls_name, config in items(style.config): # First validate the top level classes if cls_name not in classes_by_name: invalid_class_names.append((style_name, cls_name)) continue # Then validate the shortcuts for shortcut_name in keys(config.get('shortcuts', {})): if shortcut_name not in shortcuts_available_by_class_name[ cls_name]: non_existent_shortcut_names.append( (style_name, cls_name, shortcut_name)) if invalid_class_names or non_existent_shortcut_names: invalid_class_names_str = '\n'.join( f' Style: {style_name} - class: {cls_name}' for style_name, cls_name in invalid_class_names) if invalid_class_names_str: invalid_class_names_str = 'Invalid class names:\n' + invalid_class_names_str invalid_shortcut_names_str = '\n'.join( f' Style: {style_name} - class: {cls_name} - shortcut: {shortcut_name}' for style_name, cls_name, shortcut_name in non_existent_shortcut_names) if invalid_shortcut_names_str: invalid_shortcut_names_str = 'Invalid shortcut names:\n' + invalid_shortcut_names_str raise InvalidStyleConfigurationException('\n\n'.join( [invalid_class_names_str, invalid_shortcut_names_str]))
def test_debug_menu(): assert get_debug_menu().bind(request=req('get')).__html__( ) == '<nav style="background: white; border: 1px solid black; bottom: -1px; position: fixed; right: -1px; z-index: 100"><ul style="list-style: none"><li><a class="link" href="/code/">Code</a></li><li><a class="link" href="?/debug_tree">Tree</a></li><li onclick="window.iommi_start_pick()"><a class="link" href="#">Pick</a></li><li><a class="link" href="?_iommi_live_edit">Edit</a></li><li><a class="link" href="?_iommi_prof">Profile</a></li><li><a class="link" href="?_iommi_sql_trace">SQL trace</a></li></ul></nav>'