Customizing the Orchard navigation menu - asp.net-mvc-3

Excuse me for asking such a general question. I'm creating a website with Orchard CMS. The website's design and interactivity are critical requirements. I have a navigation menu which has a fixed size(900 px wide), but should be able to adjust as many menu items as possible (I do this manually by modifying the css). I've used a bit of jQuery to create some animations on mouse hovers etc. for the menu. Problem is that the css and jQuery parameters are hard coded. So if a user were to change to add a new menu item, they need to know in advance the number of menu items, and sub items, thus it's not very easy for the average user to customize it which the whole point of a CMS. What is the best way of keeping this menu interactive (with the jQuery animations), and such that the user can be able to add content pages to this menu (as they do with the default navigation menu in orchard) and also user friendly such that the non technical user need to have to mess around with the jQuery and CSS of the menu?
What is the best way of doing this, should I create a module (a navigation menu component?) which will dynamically set the css/jQuery values (width etc.)
UPDATE
Also, right now I have my HTML (my navigation menu, Unorderd List etc.) and its jQuery script reference embedded in my Layout.cshtml, and the style for the navigation menu is in my Site.css in my theme, is this considered bad practice?

I eventually got this done in orchard 1.5 by overriding the Menu.cshtml view, coding some logic to check the number of menu items and then rendering navigation menus with different ID's, based on the number of menu items they contained. I then added different CSS selectors in my Site.css, each with CSS suitable for navigation menus of various sizes. Here is what my Menu.cshtml ended up looking like (this goes in the Views folder of your currently active theme).
#
{
Script.Require("jQuery");
var tag = Tag(Model , "ul");
var items = (IList<dynamic>)Enumerable.Cast<dynamic>(Model.Items);
}
#{//if the menu contains 3 items, render a nav called 'ThreeItemNav'
if(items.Count == 3){
<nav id="ThreeItemNav">
#tag.StartElement
#* see MenuItem shape template *#
#DisplayChildren(Model)
#tag.EndElement
</nav>
}
else if(items.Count == 4){
<nav id="FourItemNav">
#tag.StartElement
#* see MenuItem shape template *#
#DisplayChildren(Model)
#tag.EndElement
</nav>
}
else if(items.Count == 5){
<nav id="FiveItemNav">
#tag.StartElement
#* see MenuItem shape template *#
#DisplayChildren(Model)
#tag.EndElement
</nav>
}
}
//Include the jQuery to add animations to the navigation menu
#using (Script.Foot())
{
<script type ="text/javascript">
//<![CDATA[
$(document).ready(function () {
//Add your script here
$(" #FiveItemNav li").hover(function () {
//Do something when the sub menu list items are hovered....
});
$(" #FourItemNav li").hover(function () {
//Do something when the sub menu list items are hovered....
});
$(" #ThreeItemNav li").hover(function () {
//Do something when the sub menu list items are hovered....
});
});
//]]>
</script>
}
Note that you need to add CSS selectors in your theme for each nav element (ThreeItemNav, FourItemNav and FiveItemNav), for example in your current themes Site.css:
/*Style the Three Item Navigation menu*/
#ThreeItemNav li
{
background-color:#263A79;
}
#ThreeItemNav a:hover
{
border-right:1px solid #333;
border-left:1px solid #333;
}
#ThreeItemNav > ul li.current
{
background:#5882FA;
}
/*Style the Four Item Navigation menu*/
#FourItemNav li
{
background:#Purple;
}
#FourItemNav a:hover
{
background:Orange;
}
.........more styles
This certainly seems like a long winded approach, but it's the best I could think off so that I can maintain the functionality of the Orchard navigation menu and still style it with CSS and add jQuery animations. I figured an initial development cost was worth adding some powerful capabilities to the navigation menu in the long run. I'd love to hear any suggestions on how to do this in a neater way. Also I would definitely recommend using Orchard 1.5, since it has built in support for creating hierarchical navigation menus.
Checking out the inner workings of Menu.cshtml and MenuItem.cshtml views help a lot in trying to understand how the navigation menus are rendered in Orchard as well as inspecting how the default Theme Machine styles the navigation menu and its various levels/sub menus.

Related

Scrolling to elements in CKEditor via JavaScript

My CKEditor fields often contain lots of content with h1, h2, h3, etc headings, and I've written a script that presents all the headings in a sidebar for quick reference. I'd also like to use this sidebar as a navigation menu for the editor content, so clicking a heading in the sidebar scrolls the editor to the related heading, but I can't figure out how to wire it all up.
This post at https://davidwalsh.name/scroll-element-ckeditor leads me to believe that it should be possible, but I can't figure out how to get to the "editor" element described in the post.
My sidebar is built with jQuery from a CKEditor textarea with id="content" like this...
var content = $('<div/>').append($('#content').val());
var sidebar = "";
$(content).find('h1,h2,h3,h4,h5,h6').addClass('heading');
$(content).find('.heading').each(function () {
sidebar += this.outerHTML;
});
$('#sidebar').html(sidebar);
I imagine using jQuery :contains() to identify heading elements in the editor based on the text they contain, but I can't figure out how to hook back into the CKEditor instance in a way that enables this kind of DOM activity.
I am using CKEditor 4 but am happy to upgrade to version 5 if it offers a better solution to my problem.
Thanks!
This is what wound up working for me:
var content = $('<div/>').append($('#content').val());
var sidebar = "";
$(content).find('h1,h2,h3,h4,h5,h6').addClass('heading');
$(content).find('.heading').each(function () {
sidebar += this.outerHTML;
});
$('#sidebar').html(sidebar);
$('#sidebar .heading').click(function() {
var element = $('#cke_content iframe').contents().find(':contains(' + $(this).text() + ')')[2];
if (element) {
element.scrollIntoView();
}
});

Where to init Kendo Mobile Application in Durandal

I am trying to use Kendo's Mobile widgets inside of my PhoneGap Durandal project.
See sample source project here: https://github.com/rodneyjoyce/DurandalKendoMobile/tree/master/App
I don't understand where to put the Kendo initilisation code (the widgets do not render without this):
window.kendoMobileApplication = new kendo.mobile.Application($(document.body), {
skin: "flat"
});
I have tried to put it into the Index.html, shell.html and into a particular Durandal page view (x.html), shell.js, main,js and x.js but none of these work.
My Index page has these links in it:
<script src="css/telerik/js/kendo.all.min.js"></script>
<link href="css/telerik/styles/kendo.mobile.flat.min.css" rel="stylesheet" />
My Durandal View has the following HTML with Kendo Mobile widgets:
<section>
1
<div id="kendoindex">
<div data-kendo-role="view" data-kendo-title="View">
<header data-kendo-role="header">
<div data-kendo-role="navbar">
<span data-kendo-role="view-title">Title</span>
</div>
</header>
<ul data-kendo-role="listview">
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
</div>
2
</section>
and my ViewModel for this View:
define(function (require)
{
function viewModel()
{
var self = this;
self.activate = activate;
function activate()
{
//window.kendoMobileApplication = new kendo.mobile.Application($("#kendoindex"), {
// skin: "flat"
//});
}
}
var vm = new viewModel();
return vm;
});
If I don't call kendoMobileApplication then the Kendo Mobile widgets are not rendered (it just shows "1 Title 2" with no CSS (ie. Kendo is not transforming them).
Everything seems to be keyed on where to call this: kendoMobileApplication in Durandal.
I followed the Durandal guidelines to give the Kendo bindings their own namespace: http://durandaljs.com/documentation/KendoUI/
UPDATE 1
I have created a simple Durandal 1.2 project which highlights the problem with Kendo Mobile and Durandal (and PhoneGap, but irrelevant here)), namely:
The only way to get the Mobile controls to render properly is to call kendo.mobile.Application. However this cannot find the right HTML element if it is put into a different file and loaded with Durandal.
I cannot find the right place to put this init code as it throws this error: “Uncaught Error: Your kendo mobile application element does not contain any direct child elements with data-role="view" attribute set. Make sure that you instantiate the mobile application using the correct container.”
kendoIndex.html is NOT Durandal but shows how it should be rendered if the kendo.mobile.Application is called correctly (Run this first to see what we are trying to achieve)
Shell : Has the Kendo Layout which does not get rendered.
Home View: Has the simple Kendo Mobile view – this does not get rendered.
About: A simple HTML page without Kendo
Source is on Guthub – if anyone can get Kendo Mobile working with Durandal I would appreciate it (or at least confirm if it is impossible (Telerik are no help at all on this)).
https://github.com/rodneyjoyce/DurandalKendoMobile/tree/master/App
Here's a working demo, which shows the correct start screen, but doesn't show the about view on navigation click. There's probably some extra work required to either remove kendo's or Durandal's router functionality.
http://rainerat.spirit.de/DurandalKendoMobile/App/#/
There were a couple of things required to make it work.
https://github.com/RainerAtSpirit/DurandalKendoMobile/commits/master
Kendo requires that the host element and all 'view' and 'layout' elements are in the DOM and that 'view' and 'layout' are child of the container element. After updating the view html to reflect this the right place to create the kendo app would be home.js
define(function( require ) {
var ctor = function() {
};
ctor.prototype.activate = function() {
console.log("Home activate");
};
ctor.prototype.viewAttached = function() {
var $kendoHost = $('#kendoHost');
// Workaround for height = 0.
// Additional code required to calculate on windows.resize
$kendoHost.height($(window).height());
this.app = new kendo.mobile.Application($kendoHost, {
skin: "flat"
});
console.log("Home viewAttached", this.app, $kendoHost.height());
};
return ctor;
});
Last kendo determines kendoHost height as 0, which prevents that the correctly rendered view show up. As a workaound I'm using $kendoHost.height($(window).height()); right before creating the app addresses.
As said in my comment above I'm still not sure if I'd recommend combining those two SPA frameworks as you might encounter more glitches like that while building your app. That said I'd love to hear about your progress :).

Minima.pl implementation of Isotope jQuery library?

One thing I don't understand is how did Minima.pl (http://minima.pl/pl) implement that feature within Isotope library where clicking on a thumbnail opens up a bigger gallery of images (a single clickable image, clicking on it makes it cycle through the rest of the images in a gallery) while resorting the Isotope items?
Here is how far I got -> http://tinyurl.com/cr5kzml
Anyone have any ideas on what I'm missing, how do I get this working?
Well, I am author of minima.pl website ;).
The part which takes care of repositioning of tiles after enlarging clicked one:
$('#mainContent').isotope('reLayout', function(){
$('html, body').animate({scrollTop: item.offset().top - 10}, 400);
});
It also takes care of scrolling browser window to top of clicked tile.
I am triggering the above action after loading clicked tile content (by AJAX). The trick is to trigger it simultaneously with enlarging the clicked tile.
I will be glad to answer any additional questions.
Actually, this is simple to achieve. Normally, a click on an Isotope .item can, for example, maximise it, another click minimises it. If you want interactivity inside a clicked-on Isotope .item, you simply don't attach the minimisation function to it. Instead, clicking on another Isotope .item minimises the previously selected = maximised item. By keeping track of the previously selected .item, clicks inside the maximised .item won't close it. Basic logic for an example that allows maximising and minimising only by clicking on a "header" zone inside each Isotope .item:
$(document).ready(function () {
var $container = $('#container');
$container.isotope({
itemSelector: '.item',
masonry: {
columnWidth: 128 // corresponding to .item divs width relationships
}
});
// $container.isotope('shuffle'); // randomise for every new visitor
$items = $('.item'); // to reference methods on all .item divs later
$('.header').click(function () { // instead of registering the entire .item div (default use), only its .header div (child div) receives clicks
var $previousSelected = $('.selected'); // necessary for switching
if ($(this).parent().hasClass('selected')) { // use $(this).parent() (not $(this)), because the .header div is a child of the .item div
$(this).parent().removeClass('selected');
$(this).parent().children('.maximised').hide();
$(this).parent().children('.minimised').show();
$items.find('.minimised, .header').removeClass('overlay'); // returns all .minimised divs to previous state after the .item is closed again
} else {
$previousSelected.removeClass('selected');
$previousSelected.children('.minimised').show();
$previousSelected.children('.maximised').hide();
$(this).parent().addClass('selected');
$(this).parent().children('.minimised').hide();
$(this).parent().children('.maximised').show();
$items.not('.selected').find('.minimised, .header').addClass('overlay'); // adds .overlay on each .item which is not currently .selected
}
$container.isotope('reLayout'); // comment out to mimick old masonry behaviour
});
});
The actual interactivity inside each Isotope .item can then be coded however you like; hardcoded or dynamic...
By click on a thumbnail a ajax function return the same gallery except a bigger replacement for the thumbnail. Then let isotope rearrange the gallery. You can find an example here: http://www.maxmedia.com or http://www.phpdevpad.de (my own site).

Mootools accordian remote trigger in Joomla

I'm trying to customise the Joomla 2.5 Accordion contacts page to add a clickable map to trigger the accordion and I'm struggling. The accordion part works fine, and the map works fine with clickable areas, but I can't link the two.
The mootools accordion script that triggers the accordian is this (pretty standard Joomla contacts page version exept I added 'var OfficeAccordian =' on line 2:
window.addEvent('domready', function(){
var OfficeAccordian = new Fx.Accordion(
$$('div#contact-slider.pane-sliders > .panel > h3.pane-toggler'),
$$('div#contact-slider.pane-sliders > .panel > div.pane-slider'), {
onActive: function(toggler, i) {
toggler.addClass('pane-toggler-down');
toggler.removeClass('pane-toggler');i.addClass('pane-down');
i.removeClass('pane-hide');
Cookie.write('jpanesliders_contact-slider',$$('div#contact-slider.pane-sliders > .panel > h3').indexOf(toggler));
},
onBackground: function(toggler, i) {
toggler.addClass('pane-toggler');
toggler.removeClass('pane-toggler-down');
i.addClass('pane-hide');i.removeClass('pane-down');
if($$('div#contact-slider.pane-sliders > .panel > h3').length==$$('div#contact-slider.pane-sliders > .panel > h3.pane-toggler').length)
Cookie.write('jpanesliders_contact-slider',-1);
},
duration: 300,
opacity: false,
alwaysHide: true
});
});
but the script I need to simulate a click on the accordion trigger when I click a link on the map (standard anchor button in a list) is beyond me. I've had a go at adapting a few examples from various websites, but none have worked. My javascript skills are not good enough yet to know why they aren't working or how to fix them.
(I'm not sure it needs to be mootools as I have mootools & jquery both loaded & working on the site)
The Fx.Accordion has an interface (API of methods the instance exposes so you can control it).
All it takes is reading the manual:
http://mootools.net/docs/more/Fx/Fx.Accordion#Fx-Accordion:display
OfficeAccordian.display(2);
if that is not enough, you read the source code:
https://github.com/mootools/mootools-more/blob/master/Source/Fx/Fx.Accordion.js#L157
this reveals you can pass a second argument to .display as boolean to use fx or not.
p.s. this is not how you spell accordion (OfficeAccordian). :)

How to prevent | Mozilla FireFox (3.6) ContentEditable -- applies CSS to the editable container instead of it's content

I have some page with something like this:
<div id="editor" contenteditable="true">SomeText</div>
I have an selfmade JS editor which actually issues
document.execCommand(some_command,false,optional_value);
when user presses a button in the editor. (For example I have plain, simple [Bold] button).
Everything is fine as long as I apply editing to part of "SomeText". For example selecting "Text" with mouse and pressing [Bold] button (which leads to document.execCommand("bold",false,false);) will produce:
<div id="editor" contenteditable="true">Some<span style="some-css-here">Text</span></div>
but when I select entire content of the div ("SomeText" in this example) and press [Bold] in my editor, FF will not produce expected
<div id="editor" contenteditable="true"><span style="some-css-here">SomeText</span></div>
but rather
<div id="editor" contenteditable="true" style="some-css-here">SomeText</div>
Notice the "style" attribute went into the editable div!
Why this makes a difference to me?
--It's because after editing is done I would like to take the content of the editable div, along with all styles, formating etc and further use it on the page. But I can't -- all the styling now sits inside the div.
A solution when I would be advised to extract styles from the div is not acceptable -- the div during its life takes a lot of styles from other active elements of the page (heavy jQuery usage)
So in brief:
How to tell FF to never touch editable div and apply all styling to its inner contents only?
Sincere thanks for you time.
(just pulled last of my hair, browsing FF dev site along with many others(((( )
Call once before any other execCommand and switch FF to tag mode
document.execCommand('StyleWithCSS', false, false);
Sometimes organizing and writing my thoughts brings me very positive results.
I have found satisfactory solution.
1)insert hidden div as a first child node into your editing div:
<div id="editor" contenteditable="true">
<div class="edit_text_mozilla_hack"></div>
SomeText
</div>
2) The CSS for it:
.edit_text_mozilla_hack {
display: block;
width: 0;
height: 0;
-moz-user-edit: none;
-moz-user-select: none
}
3)Now you can edit. I tested it with this my small test (actually I need all this stuff to edit short pieces of text like like captions, news subjects etc)
4)Before you use the content -- obious -- remoe that div.
5)When you want to return to editing -- insert it again.
Some bits of code from working (finally! ))) project:
//adds hidden div to all editable regions 'editables'
//the parameter is for speeding the thins up -- I'm often working with all or a lot of editable regions
function editAddMozillaHack(editables) {
if (!editables) {
editables = editGetEditables();
}
$("." + adminOptions["admin_loader"]).remove();
editables.each(function() {
$(this).prepend('<div class="edit_text_mozilla_hack"></div>')
});
}
//removes the hack from all regions
function editRemoveMozillaHack() {
$(".edit_text_mozilla_hack").remove();
}
//just returns all the editable regions -- my project often requires them all
function editGetEditables() {
return $("[contenteditable=\"true\"]");
}
of course -- testing pending.
I would like to hear from you ;)
regards.
I had the similar problem, when select all in contenteditable area with mouse or use CTRL-A there and then press CTRL+B for example, Firefox put style to the contenteditable container instead it's content.
<div contenteditable="true" style="font-weight: bold;"><p>..content..</p></div>
Same applyed for italic, font size, font-family and other inline styles.
I wrote a function which fixing that issue. It creates new element below the content and changes selected range till that element:
function checkSelectAll (container, cmd, args) {
if(document.getSelection) {
var cn = container.childNodes,
s = document.getSelection(),
r = s.getRangeAt(0);
if(r.startContainer == container && r.endContainer == container){
var endMarker = document.createElement('SPAN')
container.appendChild(endMarker);
r.setEndBefore(endMarker);
s.removeAllRanges();
s.addRange(r);
document.execCommand(cmd,false,args);
container.removeChild(endMarker);
} else {
document.execCommand(cmd,false,args);
}
} else {
document.execCommand(cmd,false,args);
}
};
this code affects only FF, for other browsers it will just apply execCommand

Resources