--- /dev/null +++ b/static/css/editor.css @@ -1 +1,329 @@ +/* + * HTML5 Boilerplate + * + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + */ + +/* ========================================================================== + Base styles: opinionated defaults + ========================================================================== */ + +html, +button, +input, +select, +textarea { + color: #222; +} + +body { + font-size: 1em; + line-height: 1.4; +} + +/* + * Remove text-shadow in selection highlight: h5bp.com/i + * These selection declarations have to be separate. + * Customize the background color to match your design. + */ + +::-moz-selection { + background: #b3d4fc; + text-shadow: none; +} + +::selection { + background: #b3d4fc; + text-shadow: none; +} + +/* + * A better looking default horizontal rule + */ + +hr { + display: block; + height: 1px; + border: 0; + border-top: 1px solid #ccc; + margin: 1em 0; + padding: 0; +} + +/* + * Remove the gap between images and the bottom of their containers: h5bp.com/i/440 + */ + +img { + vertical-align: middle; +} + +/* + * Remove default fieldset styles. + */ + +fieldset { + border: 0; + margin: 0; + padding: 0; +} + +/* + * Allow only vertical resizing of textareas. + */ + +textarea { + resize: vertical; +} + +/* ========================================================================== + Chrome Frame prompt + ========================================================================== */ + +.chromeframe { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== + Author's custom styles + ========================================================================== */ + +.page { + border: 1px solid #999; + padding: 20px; + width: 400px; + height: 400px; +} +#pages { + position: absolute; + left: 20px; + top: 20px; + border: 1px solid black; + padding: 10px; + width: 300px; + height: 80%; +} +#page { + position: absolute; + left: 500px; + top: 90px; +} +#pagelist { + padding: 10px; +} +#content { + padding: 10px; + margin: 3px; + margin-bottom: 10px; + max-height: 120px; + overflow-y: auto; +} +.content-item:hover { + cursor: pointer; + cursor: hand; +} +#contentview { + border: 1px solid #999; + padding: 10px; + margin: 10px; +} +.contentview { +} +#specific-content { + padding: 3px; + margin-bottom: 10px; +} + +/* ========================================================================== + Helper classes + ========================================================================== */ + +/* + * Image replacement + */ + +.ir { + background-color: transparent; + border: 0; + overflow: hidden; + /* IE 6/7 fallback */ + *text-indent: -9999px; +} + +.ir:before { + content: ""; + display: block; + width: 0; + height: 100%; +} + +/* + * Hide from both screenreaders and browsers: h5bp.com/u + */ + +.hidden { + display: none !important; + visibility: hidden; +} + +/* + * Hide only visually, but have it available for screenreaders: h5bp.com/v + */ + +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* + * Extends the .visuallyhidden class to allow the element to be focusable + * when navigated to via the keyboard: h5bp.com/p + */ + +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* + * Hide visually and from screenreaders, but maintain layout + */ + +.invisible { + visibility: hidden; +} + +/* + * Clearfix: contain floats + * + * For modern browsers + * 1. The space content is one way to avoid an Opera bug when the + * `contenteditable` attribute is included anywhere else in the document. + * Otherwise it causes space to appear at the top and bottom of elements + * that receive the `clearfix` class. + * 2. The use of `table` rather than `block` is only necessary if using + * `:before` to contain the top-margins of child elements. + */ + +.clearfix:before, +.clearfix:after { + content: " "; /* 1 */ + display: table; /* 2 */ +} + +.clearfix:after { + clear: both; +} + +/* + * For IE 6/7 only + * Include this rule to trigger hasLayout and contain floats. + */ + +.clearfix { + *zoom: 1; +} + +/* ========================================================================== + EXAMPLE Media Queries for Responsive Design. + Theses examples override the primary ('mobile first') styles. + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 35em) { + /* Style adjustments for viewports that meet the condition */ +} + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min-resolution: 144dpi) { + /* Style adjustments for high resolution devices */ +} + +/* ========================================================================== + Print styles. + Inlined to avoid required HTTP connection: h5bp.com/r + ========================================================================== */ + +@media print { + * { + background: transparent !important; + color: #000 !important; /* Black prints faster: h5bp.com/s */ + box-shadow:none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + /* + * Don't show links for images, or javascript/internal links + */ + + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; /* h5bp.com/t */ + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } +} --- a/static/css/main.css +++ b/static/css/main.css @@ -93,51 +93,7 @@ Author's custom styles ========================================================================== */ -.page { - border: 1px solid #999; - padding: 20px; - width: 400px; - height: 400px; -} -#pages { - position: absolute; - left: 20px; - top: 20px; - border: 1px solid black; - padding: 10px; - width: 300px; - height: 80%; -} -#page { - position: absolute; - left: 500px; - top: 90px; -} -#pagelist { - padding: 10px; -} -#content { - padding: 10px; - margin: 3px; - margin-bottom: 10px; - max-height: 120px; - overflow-y: auto; -} -.content-item:hover { - cursor: pointer; - cursor: hand; -} -#contentview { - border: 1px solid #999; - padding: 10px; - margin: 10px; -} -.contentview { -} -#specific-content { - padding: 3px; - margin-bottom: 10px; -} + /* ========================================================================== Helper classes --- a/static/js/editor.js +++ b/static/js/editor.js @@ -1,4 +1,6 @@ (function (M) { + + /* view to manage list of pages - add, remove and show them */ var PageListView = Backbone.View.extend({ tagName: 'div', className: '', @@ -77,6 +79,8 @@ url: '/edit' }); + /* view to manage each page and their properties - change page properties, + * add content, remove and show content, and update the page */ var PageView = Backbone.View.extend({ tagName: 'div', id: 'page', @@ -210,6 +214,7 @@ } }); + /* view to manage, render and update each content */ var ContentView = Backbone.View.extend({ id: 'contentview', events: { @@ -314,18 +319,34 @@ } }); + /* view to configure custom navigation menu */ + var MenuConfigView = Backbone.View.extend({ + el: '#menu-config', + events: { + }, + initialize: function() { + _.bindAll(this); + this.template = _.template($('#menu-config-template').html()); + }, + render: function() { + $('#content-container').append(this.template({ + menu: 'foo' + })); + } + }); + M.editor = { init: function() { - M.pages = new Pages(); - _.each(M.site_content, function(page) { - M.pages.add(new Page(page)); - }); + M.pages = new Pages(M.site_content); var pagelistview = new PageListView(); pagelistview.render(); M.pages.on('add', function(page) { pagelistview.render(); }); M.pagelistview = pagelistview; + + //var menuconfig = new MenuConfigView(); + //menuconfig.render(); } }; --- a/static/js/mouchak.js +++ b/static/js/mouchak.js @@ -1,4 +1,6 @@ (function(M) { +/* + * */ var types; M.types = types = {}; @@ -6,71 +8,64 @@ var AppView = Backbone.View.extend({ el: 'body', events: { + }, + initialize: function() { + _.bindAll(this); + }, + render: function() { + var navview = new NavigationView(); + navview.render(); + }, + updateBreadcrumbs: function(event) { + //TODO: write code to use bootstrap's breadcrumbs to render a + // navigational breadcrumb + } +}); + +var NavigationView = Backbone.View.extend({ + el: '#navigation', + events: { 'click .nav li a' : 'navClicked' }, initialize: function() { _.bindAll(this); + this.template = _.template($('#nav-bar-template').html()); }, render: function() { - $('#index').show(); + this.$el.append(this.template({})); + this.$ul = $('.nav'); + this.populate(); + }, + populate: function() { + var item_template = _.template($('#nav-item-template').html()); _.each(M.pages.models, function(page) { - this.createNavigation(page.id); + console.log(_.isEmpty(page.get('children'))); + this.$ul.append(item_template({ + cls: (_.isEmpty(page.get('children'))) ? '' : 'dropdown', + page: page.get('name') + })); }, this); + this.$links = $('.nav li'); + //console.log(this.$links[0]); + $(this.$links[0]).addClass('active'); }, navClicked: function(event) { - $('.nav li').removeClass('active'); + console.log('navClicked'); + this.$links.removeClass('active'); $(event.currentTarget).parent().addClass('active'); - }, - createNavigation: function(pageid) { - var li, page = M.pages.get(pageid); - console.log(page.id, page.get('name')); - if(page.get('name') === 'index' && !_.isEmpty(page.get('children'))) { - var id = nameIdMap['index']; - li = '
  • Home
  • '; - $('#nav-'+ id +' .nav').append(li); - } - var dropdown_template = _.template($('#nav-dropdown-template').html()); - var children = page.get('children'); - _.each(children, function(child) { - console.log('children of ', page.get('name'), child); - var id = nameIdMap[child]; - var model = M.pages.get(id); - if(!_.isObject(model)) { - console.log('Error: Cannot find page '+ child +' which is defined as children of ' + page.get('name')); - return false; - } - var children = model.get('children'); - if(_.isEmpty(children)) { - li = '
  • ' + M.humanReadable(child) + '
  • '; - console.log('li: ', li); - } - else { - li = dropdown_template({ - name: M.humanReadable(model.get('name')), - list: _.map(children, M.humanReadable) - }); - } - console.log('nav el: ', $('#nav-' + page.id + ' .nav')); - //$(li).appendTo('#nav-' + page + ' .nav'); - $('#nav-'+ page.id +' .nav').append(li); - }); - }, - updateBreadcrumbs: function(event) { - //TODO: write code to use bootstrap's breadcrumbs to render a - // navigational breadcrumb } }); var AppRouter = Backbone.Router.extend({ routes : { - 'index' : 'index', + //'index' : 'index', ':page' : 'showPage' }, - index: function() { + /*index: function() { $('.pageview').hide(); var id = nameIdMap['index']; $('#'+id).show(); - }, + },*/ showPage: function(page) { $('.pageview').hide(); //news pages are rendered on the fly, @@ -88,46 +83,53 @@ // their names var nameIdMap = {}; -/* Defining other necessary functions */ +// initialize the app M.init = function() { M.tags = {}; //global tag cache - M.pages = new M.types.model.Pages(); //global collection of all pages - // iterate through the JSON to intialize models and views - _.each(M.site_content, function(page) { - var new_page = new M.types.model.Page(page); + // global collection of pages + M.pages = new types.model.Pages(M.site_content); + + // iterate through pages to get their content and render them using views and + // models + _.each(M.pages.models, function(page) { var contents = []; - _.each(page.content, function(content) { + _.each(page.get('content'), function(content, idx) { + // empty content! if(_.isEmpty(content)) { - console.log('Empty content for ' + page.name); + console.log('NOTICE: Empty content for ' + page.get('name') + ' at ' + + idx); return; } - var Item = types.model[content.type]; - if(!Item) { - console.log('Error: Invalid type '+ content.type +' for ', content); + + var ContentModel = types.model[content.type]; + if(!ContentModel) { + throw new Error('Invalid type. Not a Mouchak type: ' + content.type); return; } - var item = new Item(content); - contents.push(item); - M.createTagList(content, item); - }); - new_page.set({content: contents}); - var new_page_view = new M.types.view.PageView({model: new_page, - id: new_page.get('id')}); - M.pages.add(new_page); - nameIdMap[new_page.get('name')] = new_page.id; - }); + var contentmodel = new ContentModel(content); + contents.push(contentmodel); + //index the tags in the content + M.createTagList(content, contentmodel); + }); + page.set({content: contents}); + var pageview = new types.view.PageView({model: page, id: page.id}); + + // prepare the name to id map + nameIdMap[page.get('name')] = page.id; + }); + M.appView = new AppView(); M.appView.render(); + var app_router = new AppRouter(); Backbone.history.start(); - app_router.navigate('index', {trigger: true}); - // start with index page - //var location = window.location; - /*location.href = location.protocol + '//' + location.hostname + - location.pathname + '#/index';*/ - M.simHeir(); + + var startpage = M.pages.models[0].get('name'); + app_router.navigate(startpage, {trigger: true}); + + //M.simHeir(); }; // hack to simulate heirarchy among the page views @@ -165,7 +167,8 @@ // @tags should be an array M.filterTags = function(tags) { if(!_.isArray(tags)) { - console.log('You have to pass an array'); //TODO: raise an exception + //console.log('You have to pass an array'); //TODO: raise an exception + throw new Error(' accepts only an array of strings'); return false; } var list = []; --- a/static/js/views.js +++ b/static/js/views.js @@ -121,7 +121,7 @@ }, render: function() { $('#content-container').append(this.el); - this.appendNavTemplate(); + //this.appendNavTemplate(); $(this.el).append('

    '+this.model.get('title')+'

    '); var self = this; _.each(this.model.get('content'), function(item) { --- a/templates/editor.html +++ b/templates/editor.html @@ -14,7 +14,7 @@ - + @@ -23,12 +23,14 @@
    - -
    -
    + + +
    + +
    - + + + - + + + + +