Commit d296833227649048a6852577b41c134b0fe38a8a

Navigation Menu changes and CSS fix

- Nav menu is a seperate component now which can be customized. By default it
  does not support sub-menus or dropdowns. But the user can write her own
bootstrap nav component. Support for adding custom menu has to be added.
- CSS for the editor is a seperate file now so that website's main css is not
  used in the editor.
  • static/css/editor.css 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • static/css/main.css 46 ---------------------------------------------+
  • static/js/editor.js 29 ----+++++++++++++++++++++++++
  • static/js/mouchak.js 145 -----------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • static/js/views.js 2 -+
  • templates/editor.html 21 -----++++++++++++++++
  • templates/index.html 29 ---++++++++++++++++++++++++++
  • Diff rendering mode:
  • inline
  • side by side

static/css/editor.css

1/*
2 * HTML5 Boilerplate
3 *
4 * What follows is the result of much research on cross-browser styling.
5 * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
6 * Kroc Camen, and the H5BP dev community and team.
7 */
8
9/* ==========================================================================
10 Base styles: opinionated defaults
11 ========================================================================== */
12
13html,
14button,
15input,
16select,
17textarea {
18 color: #222;
19}
20
21body {
22 font-size: 1em;
23 line-height: 1.4;
24}
25
26/*
27 * Remove text-shadow in selection highlight: h5bp.com/i
28 * These selection declarations have to be separate.
29 * Customize the background color to match your design.
30 */
31
32::-moz-selection {
33 background: #b3d4fc;
34 text-shadow: none;
35}
36
37::selection {
38 background: #b3d4fc;
39 text-shadow: none;
40}
41
42/*
43 * A better looking default horizontal rule
44 */
45
46hr {
47 display: block;
48 height: 1px;
49 border: 0;
50 border-top: 1px solid #ccc;
51 margin: 1em 0;
52 padding: 0;
53}
54
55/*
56 * Remove the gap between images and the bottom of their containers: h5bp.com/i/440
57 */
58
59img {
60 vertical-align: middle;
61}
62
63/*
64 * Remove default fieldset styles.
65 */
66
67fieldset {
68 border: 0;
69 margin: 0;
70 padding: 0;
71}
72
73/*
74 * Allow only vertical resizing of textareas.
75 */
76
77textarea {
78 resize: vertical;
79}
80
81/* ==========================================================================
82 Chrome Frame prompt
83 ========================================================================== */
84
85.chromeframe {
86 margin: 0.2em 0;
87 background: #ccc;
88 color: #000;
89 padding: 0.2em 0;
90}
91
92/* ==========================================================================
93 Author's custom styles
94 ========================================================================== */
95
96.page {
97 border: 1px solid #999;
98 padding: 20px;
99 width: 400px;
100 height: 400px;
101}
102#pages {
103 position: absolute;
104 left: 20px;
105 top: 20px;
106 border: 1px solid black;
107 padding: 10px;
108 width: 300px;
109 height: 80%;
110}
111#page {
112 position: absolute;
113 left: 500px;
114 top: 90px;
115}
116#pagelist {
117 padding: 10px;
118}
119#content {
120 padding: 10px;
121 margin: 3px;
122 margin-bottom: 10px;
123 max-height: 120px;
124 overflow-y: auto;
125}
126.content-item:hover {
127 cursor: pointer;
128 cursor: hand;
129}
130#contentview {
131 border: 1px solid #999;
132 padding: 10px;
133 margin: 10px;
134}
135.contentview {
136}
137#specific-content {
138 padding: 3px;
139 margin-bottom: 10px;
140}
141
142/* ==========================================================================
143 Helper classes
144 ========================================================================== */
145
146/*
147 * Image replacement
148 */
149
150.ir {
151 background-color: transparent;
152 border: 0;
153 overflow: hidden;
154 /* IE 6/7 fallback */
155 *text-indent: -9999px;
156}
157
158.ir:before {
159 content: "";
160 display: block;
161 width: 0;
162 height: 100%;
163}
164
165/*
166 * Hide from both screenreaders and browsers: h5bp.com/u
167 */
168
169.hidden {
170 display: none !important;
171 visibility: hidden;
172}
173
174/*
175 * Hide only visually, but have it available for screenreaders: h5bp.com/v
176 */
177
178.visuallyhidden {
179 border: 0;
180 clip: rect(0 0 0 0);
181 height: 1px;
182 margin: -1px;
183 overflow: hidden;
184 padding: 0;
185 position: absolute;
186 width: 1px;
187}
188
189/*
190 * Extends the .visuallyhidden class to allow the element to be focusable
191 * when navigated to via the keyboard: h5bp.com/p
192 */
193
194.visuallyhidden.focusable:active,
195.visuallyhidden.focusable:focus {
196 clip: auto;
197 height: auto;
198 margin: 0;
199 overflow: visible;
200 position: static;
201 width: auto;
202}
203
204/*
205 * Hide visually and from screenreaders, but maintain layout
206 */
207
208.invisible {
209 visibility: hidden;
210}
211
212/*
213 * Clearfix: contain floats
214 *
215 * For modern browsers
216 * 1. The space content is one way to avoid an Opera bug when the
217 * `contenteditable` attribute is included anywhere else in the document.
218 * Otherwise it causes space to appear at the top and bottom of elements
219 * that receive the `clearfix` class.
220 * 2. The use of `table` rather than `block` is only necessary if using
221 * `:before` to contain the top-margins of child elements.
222 */
223
224.clearfix:before,
225.clearfix:after {
226 content: " "; /* 1 */
227 display: table; /* 2 */
228}
229
230.clearfix:after {
231 clear: both;
232}
233
234/*
235 * For IE 6/7 only
236 * Include this rule to trigger hasLayout and contain floats.
237 */
238
239.clearfix {
240 *zoom: 1;
241}
242
243/* ==========================================================================
244 EXAMPLE Media Queries for Responsive Design.
245 Theses examples override the primary ('mobile first') styles.
246 Modify as content requires.
247 ========================================================================== */
248
249@media only screen and (min-width: 35em) {
250 /* Style adjustments for viewports that meet the condition */
251}
252
253@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
254 only screen and (min-resolution: 144dpi) {
255 /* Style adjustments for high resolution devices */
256}
257
258/* ==========================================================================
259 Print styles.
260 Inlined to avoid required HTTP connection: h5bp.com/r
261 ========================================================================== */
262
263@media print {
264 * {
265 background: transparent !important;
266 color: #000 !important; /* Black prints faster: h5bp.com/s */
267 box-shadow:none !important;
268 text-shadow: none !important;
269 }
270
271 a,
272 a:visited {
273 text-decoration: underline;
274 }
275
276 a[href]:after {
277 content: " (" attr(href) ")";
278 }
279
280 abbr[title]:after {
281 content: " (" attr(title) ")";
282 }
283
284 /*
285 * Don't show links for images, or javascript/internal links
286 */
287
288 .ir a:after,
289 a[href^="javascript:"]:after,
290 a[href^="#"]:after {
291 content: "";
292 }
293
294 pre,
295 blockquote {
296 border: 1px solid #999;
297 page-break-inside: avoid;
298 }
299
300 thead {
301 display: table-header-group; /* h5bp.com/t */
302 }
303
304 tr,
305 img {
306 page-break-inside: avoid;
307 }
308
309 img {
310 max-width: 100% !important;
311 }
312
313 @page {
314 margin: 0.5cm;
315 }
316
317 p,
318 h2,
319 h3 {
320 orphans: 3;
321 widows: 3;
322 }
323
324 h2,
325 h3 {
326 page-break-after: avoid;
327 }
328}

static/css/main.css

93 Author's custom styles93 Author's custom styles
94 ========================================================================== */94 ========================================================================== */
9595
96.page {
97 border: 1px solid #999;
98 padding: 20px;
99 width: 400px;
100 height: 400px;
101}
102#pages {
103 position: absolute;
104 left: 20px;
105 top: 20px;
106 border: 1px solid black;
107 padding: 10px;
108 width: 300px;
109 height: 80%;
110}
111#page {
112 position: absolute;
113 left: 500px;
114 top: 90px;
115}
116#pagelist {
117 padding: 10px;
118}
119#content {
120 padding: 10px;
121 margin: 3px;
122 margin-bottom: 10px;
123 max-height: 120px;
124 overflow-y: auto;
125}
126.content-item:hover {
127 cursor: pointer;
128 cursor: hand;
129}
130#contentview {
131 border: 1px solid #999;
132 padding: 10px;
133 margin: 10px;
134}
135.contentview {
136}
137#specific-content {
138 padding: 3px;
139 margin-bottom: 10px;
140}
96
14197
142/* ==========================================================================98/* ==========================================================================
143 Helper classes99 Helper classes

static/js/editor.js

1(function (M) {1(function (M) {
2
3 /* view to manage list of pages - add, remove and show them */
2 var PageListView = Backbone.View.extend({4 var PageListView = Backbone.View.extend({
3 tagName: 'div',5 tagName: 'div',
4 className: '',6 className: '',
79 url: '/edit'79 url: '/edit'
80 });80 });
8181
82 /* view to manage each page and their properties - change page properties,
83 * add content, remove and show content, and update the page */
82 var PageView = Backbone.View.extend({84 var PageView = Backbone.View.extend({
83 tagName: 'div',85 tagName: 'div',
84 id: 'page',86 id: 'page',
214 }214 }
215 });215 });
216216
217 /* view to manage, render and update each content */
217 var ContentView = Backbone.View.extend({218 var ContentView = Backbone.View.extend({
218 id: 'contentview',219 id: 'contentview',
219 events: {220 events: {
319 }319 }
320 });320 });
321321
322 /* view to configure custom navigation menu */
323 var MenuConfigView = Backbone.View.extend({
324 el: '#menu-config',
325 events: {
326 },
327 initialize: function() {
328 _.bindAll(this);
329 this.template = _.template($('#menu-config-template').html());
330 },
331 render: function() {
332 $('#content-container').append(this.template({
333 menu: 'foo'
334 }));
335 }
336 });
337
322 M.editor = {338 M.editor = {
323 init: function() {339 init: function() {
324 M.pages = new Pages();
325 _.each(M.site_content, function(page) {
326 M.pages.add(new Page(page));
327 });
340 M.pages = new Pages(M.site_content);
328 var pagelistview = new PageListView();341 var pagelistview = new PageListView();
329 pagelistview.render();342 pagelistview.render();
330 M.pages.on('add', function(page) {343 M.pages.on('add', function(page) {
331 pagelistview.render();344 pagelistview.render();
332 });345 });
333 M.pagelistview = pagelistview;346 M.pagelistview = pagelistview;
347
348 //var menuconfig = new MenuConfigView();
349 //menuconfig.render();
334 }350 }
335 };351 };
336352

static/js/mouchak.js

1(function(M) {1(function(M) {
2/*
3 * */
24
3var types;5var types;
4M.types = types = {};6M.types = types = {};
8var AppView = Backbone.View.extend({8var AppView = Backbone.View.extend({
9 el: 'body',9 el: 'body',
10 events: {10 events: {
11 },
12 initialize: function() {
13 _.bindAll(this);
14 },
15 render: function() {
16 var navview = new NavigationView();
17 navview.render();
18 },
19 updateBreadcrumbs: function(event) {
20 //TODO: write code to use bootstrap's breadcrumbs to render a
21 // navigational breadcrumb
22 }
23});
24
25var NavigationView = Backbone.View.extend({
26 el: '#navigation',
27 events: {
11 'click .nav li a' : 'navClicked'28 'click .nav li a' : 'navClicked'
12 },29 },
13 initialize: function() {30 initialize: function() {
14 _.bindAll(this);31 _.bindAll(this);
32 this.template = _.template($('#nav-bar-template').html());
15 },33 },
16 render: function() {34 render: function() {
17 $('#index').show();
35 this.$el.append(this.template({}));
36 this.$ul = $('.nav');
37 this.populate();
38 },
39 populate: function() {
40 var item_template = _.template($('#nav-item-template').html());
18 _.each(M.pages.models, function(page) {41 _.each(M.pages.models, function(page) {
19 this.createNavigation(page.id);
42 console.log(_.isEmpty(page.get('children')));
43 this.$ul.append(item_template({
44 cls: (_.isEmpty(page.get('children'))) ? '' : 'dropdown',
45 page: page.get('name')
46 }));
20 }, this);47 }, this);
48 this.$links = $('.nav li');
49 //console.log(this.$links[0]);
50 $(this.$links[0]).addClass('active');
21 },51 },
22 navClicked: function(event) {52 navClicked: function(event) {
23 $('.nav li').removeClass('active');
53 console.log('navClicked');
54 this.$links.removeClass('active');
24 $(event.currentTarget).parent().addClass('active');55 $(event.currentTarget).parent().addClass('active');
25 },
26 createNavigation: function(pageid) {
27 var li, page = M.pages.get(pageid);
28 console.log(page.id, page.get('name'));
29 if(page.get('name') === 'index' && !_.isEmpty(page.get('children'))) {
30 var id = nameIdMap['index'];
31 li = '<li class="active"><a href="#/index"> Home </a></li>';
32 $('#nav-'+ id +' .nav').append(li);
33 }
34 var dropdown_template = _.template($('#nav-dropdown-template').html());
35 var children = page.get('children');
36 _.each(children, function(child) {
37 console.log('children of ', page.get('name'), child);
38 var id = nameIdMap[child];
39 var model = M.pages.get(id);
40 if(!_.isObject(model)) {
41 console.log('Error: Cannot find page '+ child +' which is defined as children of ' + page.get('name'));
42 return false;
43 }
44 var children = model.get('children');
45 if(_.isEmpty(children)) {
46 li = '<li><a href="#/' + child + '">' + M.humanReadable(child) + '</a></li>';
47 console.log('li: ', li);
48 }
49 else {
50 li = dropdown_template({
51 name: M.humanReadable(model.get('name')),
52 list: _.map(children, M.humanReadable)
53 });
54 }
55 console.log('nav el: ', $('#nav-' + page.id + ' .nav'));
56 //$(li).appendTo('#nav-' + page + ' .nav');
57 $('#nav-'+ page.id +' .nav').append(li);
58 });
59 },
60 updateBreadcrumbs: function(event) {
61 //TODO: write code to use bootstrap's breadcrumbs to render a
62 // navigational breadcrumb
63 }56 }
64});57});
6558
66var AppRouter = Backbone.Router.extend({59var AppRouter = Backbone.Router.extend({
67 routes : {60 routes : {
68 'index' : 'index',
61 //'index' : 'index',
69 ':page' : 'showPage'62 ':page' : 'showPage'
70 },63 },
71 index: function() {
64 /*index: function() {
72 $('.pageview').hide();65 $('.pageview').hide();
73 var id = nameIdMap['index'];66 var id = nameIdMap['index'];
74 $('#'+id).show();67 $('#'+id).show();
75 },
68 },*/
76 showPage: function(page) {69 showPage: function(page) {
77 $('.pageview').hide();70 $('.pageview').hide();
78 //news pages are rendered on the fly,71 //news pages are rendered on the fly,
83// their names83// their names
84var nameIdMap = {};84var nameIdMap = {};
8585
86/* Defining other necessary functions */
86// initialize the app
87M.init = function() {87M.init = function() {
88 M.tags = {}; //global tag cache88 M.tags = {}; //global tag cache
89 M.pages = new M.types.model.Pages(); //global collection of all pages
9089
91 // iterate through the JSON to intialize models and views
92 _.each(M.site_content, function(page) {
93 var new_page = new M.types.model.Page(page);
90 // global collection of pages
91 M.pages = new types.model.Pages(M.site_content);
92
93 // iterate through pages to get their content and render them using views and
94 // models
95 _.each(M.pages.models, function(page) {
94 var contents = [];96 var contents = [];
95 _.each(page.content, function(content) {
97 _.each(page.get('content'), function(content, idx) {
98 // empty content!
96 if(_.isEmpty(content)) {99 if(_.isEmpty(content)) {
97 console.log('Empty content for ' + page.name);
100 console.log('NOTICE: Empty content for ' + page.get('name') + ' at ' +
101 idx);
98 return;102 return;
99 }103 }
100 var Item = types.model[content.type];
101 if(!Item) {
102 console.log('Error: Invalid type '+ content.type +' for ', content);
104
105 var ContentModel = types.model[content.type];
106 if(!ContentModel) {
107 throw new Error('Invalid type. Not a Mouchak type: ' + content.type);
103 return;108 return;
104 }109 }
105 var item = new Item(content);
106 contents.push(item);
107 M.createTagList(content, item);
108 });
109 new_page.set({content: contents});
110 var new_page_view = new M.types.view.PageView({model: new_page,
111 id: new_page.get('id')});
112 M.pages.add(new_page);
113 nameIdMap[new_page.get('name')] = new_page.id;
114 });
110 var contentmodel = new ContentModel(content);
111 contents.push(contentmodel);
112 //index the tags in the content
113 M.createTagList(content, contentmodel);
114 });
115115
116 page.set({content: contents});
117 var pageview = new types.view.PageView({model: page, id: page.id});
118
119 // prepare the name to id map
120 nameIdMap[page.get('name')] = page.id;
121 });
122
116 M.appView = new AppView();123 M.appView = new AppView();
117 M.appView.render();124 M.appView.render();
125
118 var app_router = new AppRouter();126 var app_router = new AppRouter();
119 Backbone.history.start();127 Backbone.history.start();
120 app_router.navigate('index', {trigger: true});
121 // start with index page
122 //var location = window.location;
123 /*location.href = location.protocol + '//' + location.hostname +
124 location.pathname + '#/index';*/
125 M.simHeir();
128
129 var startpage = M.pages.models[0].get('name');
130 app_router.navigate(startpage, {trigger: true});
131
132 //M.simHeir();
126};133};
127134
128// hack to simulate heirarchy among the page views135// hack to simulate heirarchy among the page views
167// @tags should be an array167// @tags should be an array
168M.filterTags = function(tags) {168M.filterTags = function(tags) {
169 if(!_.isArray(tags)) {169 if(!_.isArray(tags)) {
170 console.log('You have to pass an array'); //TODO: raise an exception
170 //console.log('You have to pass an array'); //TODO: raise an exception
171 throw new Error(' accepts only an array of strings');
171 return false;172 return false;
172 }173 }
173 var list = [];174 var list = [];

static/js/views.js

121 },121 },
122 render: function() {122 render: function() {
123 $('#content-container').append(this.el);123 $('#content-container').append(this.el);
124 this.appendNavTemplate();
124 //this.appendNavTemplate();
125 $(this.el).append('<h3>'+this.model.get('title')+'</h3>');125 $(this.el).append('<h3>'+this.model.get('title')+'</h3>');
126 var self = this;126 var self = this;
127 _.each(this.model.get('content'), function(item) {127 _.each(this.model.get('content'), function(item) {

templates/editor.html

1414
15 <link rel="stylesheet" href="/static/css/normalize.css">15 <link rel="stylesheet" href="/static/css/normalize.css">
16 <link rel="stylesheet" href="/static/css/bootstrap.css">16 <link rel="stylesheet" href="/static/css/bootstrap.css">
17 <link rel="stylesheet" href="/static/css/main.css">
17 <link rel="stylesheet" href="/static/css/editor.css">
18 <script src="/static/js/lib/modernizr-2.6.1.min.js"></script>18 <script src="/static/js/lib/modernizr-2.6.1.min.js"></script>
19 </head>19 </head>
20 <body>20 <body>
23 <![endif]-->23 <![endif]-->
2424
25 <div class="container" id="container">25 <div class="container" id="container">
26 <div id="header"></div>
27 <div id="content-container">
28 <div class="clearfix"></div>
26 <div id="header"> </div>
27
28 <div id="content-container"></div>
29
30 <div id="footer">
29 </div>31 </div>
30 </div>32 </div>
31 <div id="footer"></div>
33
3234
33 <script>35 <script>
34 // initialize editor36 // initialize editor
53 <script src="/static/js/editor.js"></script>53 <script src="/static/js/editor.js"></script>
5454
55 <!-- Underscore templates -->55 <!-- Underscore templates -->
56 <script type="text/template" id="menu-config-template">
57 <div id="menu-config">
58 <textarea id="menu">
59 <%= menu %>
60 </textarea>
61 </div>
62 </script>
63
56 <script type="text/template" id="page-list-template">64 <script type="text/template" id="page-list-template">
57 <div id="pagelistview">65 <div id="pagelistview">
58 <h4> List of Pages </h4>66 <h4> List of Pages </h4>
59 <div id="pagelist"></div>67 <div id="pagelist"></div>
60 <button class="btn btn-primary pull-right" id="addPage">Add Page</button>68 <button class="btn btn-primary pull-right" id="addPage">Add Page</button>
69 <p><a href="/" class="btn"> Go to site </a></p>
61 </div>70 </div>
62 </script>71 </script>
63 <script type="text/template" id="page-list-item-template">72 <script type="text/template" id="page-list-item-template">

templates/index.html

23 <![endif]-->23 <![endif]-->
2424
25 <div class="container" id="container">25 <div class="container" id="container">
26 <div id="header"></div>
26 <div id="header">
27 <div id="navigation"></div>
28 </div>
27 <div id="content-container"></div>29 <div id="content-container"></div>
28 <div id="footer"></div>30 <div id="footer"></div>
29 </div>31 </div>
61 </div>61 </div>
62 </script>62 </script>
6363
64 <script type="text/template" id="nav-template">
65 <div class="navigation" id="nav-<%= page %>">
64 <!-- navigation templates
65 the ones with .nav-type can be used interchangbly to render different
66 kinds of navigation bar / components
67 one can add more
68 -->
69 <script type="text/template" id="nav-bar-template" class="nav-type">
70 <div class="navbar">
71 <div class="navbar-inner">
72 <!--a class="brand" href="#">Title</a-->
73 <ul class="nav"></ul>
74 </div>
75 </div>
76 </script>
77
78 <script type="text/template" id="nav-pills-template" class="nav-type">
79 <div class="navigation">
66 <ul class="nav nav-pills"></ul>80 <ul class="nav nav-pills"></ul>
67 </div>81 </div>
68 </script>82 </script>
83
84 <script type="text/template" id="nav-item-template">
85 <li class="<%= cls %>">
86 <a href="#/<%=page%>"> <%= M.humanReadable(page) %> </a>
87 </li>
88 </script>
89
69 <script type="text/template" id="nav-dropdown-template">90 <script type="text/template" id="nav-dropdown-template">
70 <li class="dropdown">91 <li class="dropdown">
71 <a class="dropdown-toggle" data-toggle="dropdown" href="#/<%= M.sanitize(name) %>"92 <a class="dropdown-toggle" data-toggle="dropdown" href="#/<%= M.sanitize(name) %>"