Beyond the DOM:
            Sane Structure for JS Apps
            Rebecca Murphey • @rmurphey • BVJS 2012

Wednesday, March 7, 12
github.com/rmurphey/bvjs




Wednesday, March 7, 12
function ObjInlineDown(e) {
                       if( is.ie ) e = event
                       if( is.ie && !is.ieMac && e.button!=1 && e.button!=2 ) return
                       if( is.ieMac && e.button != 0 ) return
                       if( is.ns && !is.ns4 && e.button!=0 && e.button!=2 ) return
                       if( is.ns4 && e.which!=1 && e.which!=3 ) return
                       this.onSelect()
                       this.onDown()
                     }

                     function ObjInlineUp(e) {
                       if( is.ie ) e = event
                       if( is.ie && !is.ieMac && e.button!=1 && e.button!=2 ) return
                       if( is.ieMac && e.button!=0 ) return
                       if( is.ns && !is.ns4 && !is.nsMac && e.button!=0 && e.button!=2 ) return
                       if( is.ns4 && e.which!=1 && e.which!=3 ) return
                       if( ( !is.ns4 && e.button==2 ) || ( is.ns4 && e.which==3 ) )
                       {
                         if( this.hasOnRUp )
                         {
                           document.oncontextmenu = ocmNone
                           this.onRUp()
                           setTimeout( "document.oncontextmenu = ocmOrig", 100)
                         }
                       }
                       else if( this.hasOnUp )
                         this.onUp()
                     }




Wednesday, March 7, 12
Wednesday, March 7, 12
Wednesday, March 7, 12
<!doctype html>
                     <html lang="en">
                     <head>
                       <meta charset="utf-8">
                       <title>Twitter Search</title>
                       <link rel="stylesheet" href="/assets/css/index.css">
                     </head>

                     <body>
                       <h1>Twitter Search</h1>
                       <div id="searchInput">
                         <form>
                            <input placeholder="enter your search term">
                            <button>submit</button>
                         </form>
                       </div>
                       <ul id="searchResults"></ul>

                       <script src="/assets/js/libs/jquery.js"></script>
                       <script src="/assets/js/app.js"></script>
                     </body>
                     </html>




Wednesday, March 7, 12
$('#searchInput form').submit(function(e){
                       e.preventDefault();

                         var term = $('#searchInput input').val(),
                             req = $.getJSON('http://search.twitter.com/search.json?callback=?&q=' +
                                   escape(term));

                         req.then(function(resp) {
                           var resultsHtml = $.map(resp.results, function(r) {
                             return '<li>' +
                               '<p class="tweet">' + r.text + '</p>' +
                               '<p class="username">' + r.from_user + '</p>' +
                             '</li>';
                           }).join('');

                         $('#searchResults').html(resultsHtml);
                       });
                     });




Wednesday, March 7, 12
Wednesday, March 7, 12
// NAVIGATION
               function togglePage(section) {
                   // if the clicked section is already the current section AND we're in full page mode
                   // minimize the current tab
                   if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' a').hasClass('md_fullpage')) {
                       // alert('clicked section is current section AND fullpage mode is active; teaser should load');
                   // Minimize
                       jQuery('#md_tabs_navigation a').removeClass('md_fullpage');
                       jQuery('.md_body').hide();
                       jQuery('#md_feature').slideDown('normal',function(){
                           var bodyContent = jQuery('#md_body_'+ section);
                            bodyContent.fadeOut('normal',function(){
                                jQuery('#md_tabs_navigation a').each(function(){
                                    var thisSection = jQuery(this).html().replace('<span<','').replace('</span<','');
                                    var thisSection_comp = thisSection.toLowerCase().replace(' ','_');
                                    jQuery('#md_body_'+ thisSection_comp).load(
                                        '/app/modules/info/loadTeaser.php?sect='+ thisSection_comp,
                                        function(){
                                            tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox');
                                            bodyContent.animate({ height: 'toggle', opacity: 'toggle' },"slow");
                                        }
                                    );
                                });
                            });
                       });
                       jQuery('#expandtabs span').empty().append('Expand Tabs');
                   } else {
                   // if the clicked section is NOT the current section OR we're NOT in full page mode
                   // then let's go to full page mode and show the whole tab
                   // Maximize
                       // alert('clicked section is not the current section OR full page mode is not active; full section should load');
                       jQuery('#md_tabs_navigation li').removeClass('current');
                       jQuery('#md_tab_'+ section).addClass('current');
                       jQuery('#md_tabs_navigation a').addClass('md_fullpage');
                       jQuery('.md_body').hide();
                       jQuery('#md_feature').slideUp('normal',function(){
                           var bodyContent = jQuery('#md_body_'+ section);
                            bodyContent.fadeOut('normal',function(){
                                bodyContent.empty();
                                var pageLoader = 'info/loadSection.php?sect='+ section;
                                if (section == 'contact_us') {
                                     pageLoader = 'contact/loadContactForm.php?form_id=1';
                                }
                                bodyContent.load('/app/modules/'+ pageLoader,function(){
                                    // ADD THICKBOXES
                                    tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox');
                                    $recent_news_links = jQuery('ul.md_news li a.recent_news_link');
                                    $recent_news_links
                                        .unbind('click')
                                        .each(function(){
                                            var hrefMod = this.href;
                                            hrefMod = hrefMod.replace(/article/,'loadNews').replace(/storyid/,'id');
                                            this.href = hrefMod;
                                        })
                                        .click(function(){
                                            var t = this.title || this.name || null;
                                            var a = this.href || this.alt;
                                            var g = this.rel || false;
                                            tb_show(t,a,g);
                                            this.blur();
                                            return false;
                                        });
Wednesday, March 7, 12              // ACCORDION
$('#btn-co-complete').live('click', function() {
            jobCompleted = true;
            $.mobile.changePage('#page-clockout-deficiency', {changeHash: false});
        });

        $('#btn-co-nocomplete').live('click', function() {
            jobCompleted = false;
            $.mobile.changePage('#page-clockout-deficiency', {changeHash: false});
        });

        $('#btn-co-nodef').live('click', function() {
            clockOut(activeJob, { completed:jobCompleted }, DW_JOB_COMPLETED);
        });

        $('#btn-co-otherdef').live('click', function() {
            $.mobile.changePage('#page-clockout-redtag', {changeHash: false});
        });

        $('#btn-co-redtag').live('click', function() {
            clockOut(activeJob, { completed:jobCompleted, redTag:true }, DW_JOB_FOLLOWUP)
        });

        $('#btn-co-noredtag').live('click', function() {
            $('#page-clockout-resolve').page();
            $.mobile.changePage('#page-clockout-resolve', {changeHash: false});
        });

             $('#btn-clockout-resolve').live('click', function() {
                       switch ($('#page-clockout-resolve :checked').val()) {
                       case 'return':
Wednesday, March 7, 12     clockOut(activeJob, { completed:false }, DW_JOB_RETURN);
search input   $('#searchInput form').submit(function(e){
                                          e.preventDefault();

                                          var term = $('#searchInput input').val(),
                                              req = $.getJSON('http://search.twitter.com/searc
                                                    escape(term));

                                          req.then(function(resp) {
                         search data        var resultsHtml = $.map(resp.results, function(r)
                                              return '<li>' +
                                                '<p class="tweet">' + r.text + '</p>' +
                                                '<p class="username">' + r.from_user + '</p>'
                                              '</li>';
                                            }).join('');

                                            $('#searchResults').html(resultsHtml);
                     search results       });
                                        });




Wednesday, March 7, 12
Wednesday, March 7, 12
Wednesday, March 7, 12
Wednesday, March 7, 12
<script   type="text/javascript"   src="http://static.a2cdn.net/misc/jquery.js?9"></script>
               <script   type="text/javascript"   src="http://static.a2cdn.net/misc/drupal.js?9"></script>
               <script   type="text/javascript"   src="/google_analytics/googleanalytics.js?9"></script>
               <script   type="text/javascript"   src="/mollom/mollom.js?9"></script>
               <script   type="text/javascript"   src="/js/jquery-1.4.2.min.js?9"></script>
               <script   type="text/javascript"   src="/js/jquery-ui-1.8.4.custom.min.js?9"></script>
               <script   type="text/javascript"   src="/js/jquery.infieldlabel.min.js?9"></script>
               <script   type="text/javascript"   src="/js/jquery.nivo.slider.js?9"></script>
               <script   type="text/javascript"   src="/js/validations.js?9"></script>
               <script   type="text/javascript"   src="/js/site.js?9"></script>
               <script   type="text/javascript"   src="/js/noconflict.js?9"></script>




Wednesday, March 7, 12
<script data-main="app/config" src="/assets/js/libs/require.js"></script>




Wednesday, March 7, 12
require.config({
                       deps: ["main"],

                         paths: {
                           // JavaScript folders
                           libs: "../assets/js/libs",
                           plugins: "../assets/js/plugins",

                           // Libraries
                           jquery: "../assets/js/libs/jquery",
                           underscore: "../assets/js/libs/underscore",
                           backbone: "../assets/js/libs/backbone"
                       }
                     });




Wednesday, March 7, 12
define([
                       "components/page",
                       "components/search/input",
                       "components/search/results",
                     ], function(Page, SearchInput, SearchResults) {
                       return {
                          // ...
                       };
                     });




Wednesday, March 7, 12
// NAVIGATION
               function togglePage(section) {
                   // if the clicked section is already the current section AND we're in full page mode
                   // minimize the current tab
                   if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' a').hasClass('md_fullpage')) {
                       // alert('clicked section is current section AND fullpage mode is active; teaser should load');
                   // Minimize
                       jQuery('#md_tabs_navigation a').removeClass('md_fullpage');
                       jQuery('.md_body').hide();
                       jQuery('#md_feature').slideDown('normal',function(){
                           var bodyContent = jQuery('#md_body_'+ section);
                            bodyContent.fadeOut('normal',function(){
                                jQuery('#md_tabs_navigation a').each(function(){
                                    var thisSection = jQuery(this).html().replace('<span<','').replace('</span<','');
                                    var thisSection_comp = thisSection.toLowerCase().replace(' ','_');
                                    jQuery('#md_body_'+ thisSection_comp).load(
                                        '/app/modules/info/loadTeaser.php?sect='+ thisSection_comp,
                                        function(){
                                            tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox');
                                            bodyContent.animate({ height: 'toggle', opacity: 'toggle' },"slow");
                                        }
                                    );
                                });
                            });
                       });
                       jQuery('#expandtabs span').empty().append('Expand Tabs');
                   } else {
                   // if the clicked section is NOT the current section OR we're NOT in full page mode
                   // then let's go to full page mode and show the whole tab
                   // Maximize
                       // alert('clicked section is not the current section OR full page mode is not active; full section should load');
                       jQuery('#md_tabs_navigation li').removeClass('current');
                       jQuery('#md_tab_'+ section).addClass('current');
                       jQuery('#md_tabs_navigation a').addClass('md_fullpage');
                       jQuery('.md_body').hide();
                       jQuery('#md_feature').slideUp('normal',function(){
                           var bodyContent = jQuery('#md_body_'+ section);
                            bodyContent.fadeOut('normal',function(){
                                bodyContent.empty();
                                var pageLoader = 'info/loadSection.php?sect='+ section;
                                if (section == 'contact_us') {
                                     pageLoader = 'contact/loadContactForm.php?form_id=1';
                                }
                                bodyContent.load('/app/modules/'+ pageLoader,function(){
                                    // ADD THICKBOXES
                                    tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox');
                                    $recent_news_links = jQuery('ul.md_news li a.recent_news_link');
                                    $recent_news_links
                                        .unbind('click')
                                        .each(function(){
                                            var hrefMod = this.href;
                                            hrefMod = hrefMod.replace(/article/,'loadNews').replace(/storyid/,'id');
                                            this.href = hrefMod;
                                        })
                                        .click(function(){
                                            var t = this.title || this.name || null;
                                            var a = this.href || this.alt;
                                            var g = this.rel || false;
                                            tb_show(t,a,g);
                                            this.blur();
                                            return false;
                                        });
                                    // ACCORDION
                                    jQuery('div.applemenu div.submenu').hide();
                                    jQuery('div.applemenu div.silverheader < a').click(
Wednesday, March 7, 12                  function(){
component              controller


                         component




                                                 service



               components display data, observe input, and
               broadcast messages that controllers can react
               to; may also provide an API for updating


Wednesday, March 7, 12
component            controller


                         component




                                               service



               controllers set up components and broker
               communications between them, eliminating
               the need for direct communication


Wednesday, March 7, 12
component              controller


                         component




                                                 service




               services expose an API for controllers
               to retrieve and persist data


Wednesday, March 7, 12
Wednesday, March 7, 12
controller




                         twitter search




Wednesday, March 7, 12
components display data, observe input, and
               broadcast messages that controllers can react
               to; may also provide an API for updating




Wednesday, March 7, 12
define([
                 "components/base"
               ], function(Component) {
                 return Component({
                   template : "app/templates/search/input.html",
                   className : 'search-input',

                         events : {
                            "submit form"   :   "_onSubmit"
                         },

                   _onSubmit : function(e) {
                     e.preventDefault();
                     var term = $.trim(this.$el.find('input')[0].value);
                     if (!term) { return; }
                     this.trigger('search', term);
                   }
                 });
               });




Wednesday, March 7, 12
<form>
                 <input placeholder="enter your search term">
                 <button>submit</button>
               </form>




Wednesday, March 7, 12
controllers set up components and broker
               communications between them, eliminating
               the need for direct communication




Wednesday, March 7, 12
<!doctype html>
                     <html lang="en">
                     <head>
                       <meta charset="utf-8">
                       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
                       <meta name="viewport" content="width=device-width,initial-scale=1">

                         <title>Searchr</title>

                       <!-- Application styles -->
                       <link rel="stylesheet" href="/assets/css/index.css">
                     </head>

                     <body>
                       <!-- Main container -->
                       <div role="main" id="main"></div>

                       <!-- Application source -->
                       <script data-main="app/config" src="/assets/js/libs/require.js"></
                     script>
                     </body>
                     </html>




Wednesday, March 7, 12
var Router = Backbone.Router.extend({
                      routes: {
                         "": "twitter",
                         ":hash": "twitter"
                      },

                      twitter : function(hash) {
                        SearchController.init();
                      }
                    });




Wednesday, March 7, 12
define([
                 "components/page", "components/search/input",
                 "components/search/results", "services/twitter"
               ], function(Page, SearchInput, SearchResults, twitterService) {
                 return {
                   init : function() {
                     this.page = new Page({ template : 'app/templates/pages/search.html' });
                     this.page.render().then(_.bind(this.setupPage, this));
                   },

                         setupPage : function() {
                           var p = this.page;
                           this.searchInput = p.place(new SearchInput(), 'searchInput');
                           this.searchResults = p.place(new SearchResults(), 'searchResults');
                           this.searchInput.on('search', _.bind(this.handleSearch, this));
                         },

                         handleSearch : function(term) {
                            twitterService.query(term).then(_.bind(this.showResults, this));
                         },

                         showResults : function(results) {
                           this.searchResults.show(results);
                         }
                 };
               });




Wednesday, March 7, 12
services expose an API for controllers
               to interact with data




Wednesday, March 7, 12
define([
                 "use!backbone"
               ], function(Backbone) {
                 var TwitterResult = Backbone.Model.extend({
                   // ...
                 });

                   var TwitterResults = Backbone.Collection.extend({
                     type : 'twitter', model : TwitterResult
                   });

                   return {
                     query : function(term) {
                       var req = $.getJSON('http://search.twitter.com/search.json?callback=?&q=' +
                            escape(term)),
                            dfd = $.Deferred();

                         req.then(function(resp) {
                           dfd.resolve(new TwitterResults(resp.results));
                         });

                         return dfd.promise();
                   }
                 };
               });




Wednesday, March 7, 12
“Writing to be read means writing code ... with
               the idea that someone else will read it. is
               fact alone will make you edit and think of better
               ways to solve the problem you have at hand.”
                                       Stoyan Stefanov, “JavaScript Patterns”



Wednesday, March 7, 12
github.com/rmurphey/bvjs




Wednesday, March 7, 12
rmurphey.com • @rmurphey




Wednesday, March 7, 12

BVJS

  • 1.
    Beyond the DOM: Sane Structure for JS Apps Rebecca Murphey • @rmurphey • BVJS 2012 Wednesday, March 7, 12
  • 2.
  • 3.
    function ObjInlineDown(e) { if( is.ie ) e = event if( is.ie && !is.ieMac && e.button!=1 && e.button!=2 ) return if( is.ieMac && e.button != 0 ) return if( is.ns && !is.ns4 && e.button!=0 && e.button!=2 ) return if( is.ns4 && e.which!=1 && e.which!=3 ) return this.onSelect() this.onDown() } function ObjInlineUp(e) { if( is.ie ) e = event if( is.ie && !is.ieMac && e.button!=1 && e.button!=2 ) return if( is.ieMac && e.button!=0 ) return if( is.ns && !is.ns4 && !is.nsMac && e.button!=0 && e.button!=2 ) return if( is.ns4 && e.which!=1 && e.which!=3 ) return if( ( !is.ns4 && e.button==2 ) || ( is.ns4 && e.which==3 ) ) { if( this.hasOnRUp ) { document.oncontextmenu = ocmNone this.onRUp() setTimeout( "document.oncontextmenu = ocmOrig", 100) } } else if( this.hasOnUp ) this.onUp() } Wednesday, March 7, 12
  • 4.
  • 5.
  • 6.
    <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Twitter Search</title> <link rel="stylesheet" href="/assets/css/index.css"> </head> <body> <h1>Twitter Search</h1> <div id="searchInput"> <form> <input placeholder="enter your search term"> <button>submit</button> </form> </div> <ul id="searchResults"></ul> <script src="/assets/js/libs/jquery.js"></script> <script src="/assets/js/app.js"></script> </body> </html> Wednesday, March 7, 12
  • 7.
    $('#searchInput form').submit(function(e){ e.preventDefault(); var term = $('#searchInput input').val(), req = $.getJSON('http://search.twitter.com/search.json?callback=?&q=' + escape(term)); req.then(function(resp) { var resultsHtml = $.map(resp.results, function(r) { return '<li>' + '<p class="tweet">' + r.text + '</p>' + '<p class="username">' + r.from_user + '</p>' + '</li>'; }).join(''); $('#searchResults').html(resultsHtml); }); }); Wednesday, March 7, 12
  • 8.
  • 9.
    // NAVIGATION function togglePage(section) { // if the clicked section is already the current section AND we're in full page mode // minimize the current tab if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' a').hasClass('md_fullpage')) { // alert('clicked section is current section AND fullpage mode is active; teaser should load'); // Minimize jQuery('#md_tabs_navigation a').removeClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideDown('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ jQuery('#md_tabs_navigation a').each(function(){ var thisSection = jQuery(this).html().replace('<span<','').replace('</span<',''); var thisSection_comp = thisSection.toLowerCase().replace(' ','_'); jQuery('#md_body_'+ thisSection_comp).load( '/app/modules/info/loadTeaser.php?sect='+ thisSection_comp, function(){ tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); bodyContent.animate({ height: 'toggle', opacity: 'toggle' },"slow"); } ); }); }); }); jQuery('#expandtabs span').empty().append('Expand Tabs'); } else { // if the clicked section is NOT the current section OR we're NOT in full page mode // then let's go to full page mode and show the whole tab // Maximize // alert('clicked section is not the current section OR full page mode is not active; full section should load'); jQuery('#md_tabs_navigation li').removeClass('current'); jQuery('#md_tab_'+ section).addClass('current'); jQuery('#md_tabs_navigation a').addClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideUp('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ bodyContent.empty(); var pageLoader = 'info/loadSection.php?sect='+ section; if (section == 'contact_us') { pageLoader = 'contact/loadContactForm.php?form_id=1'; } bodyContent.load('/app/modules/'+ pageLoader,function(){ // ADD THICKBOXES tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); $recent_news_links = jQuery('ul.md_news li a.recent_news_link'); $recent_news_links .unbind('click') .each(function(){ var hrefMod = this.href; hrefMod = hrefMod.replace(/article/,'loadNews').replace(/storyid/,'id'); this.href = hrefMod; }) .click(function(){ var t = this.title || this.name || null; var a = this.href || this.alt; var g = this.rel || false; tb_show(t,a,g); this.blur(); return false; }); Wednesday, March 7, 12 // ACCORDION
  • 10.
    $('#btn-co-complete').live('click', function() { jobCompleted = true; $.mobile.changePage('#page-clockout-deficiency', {changeHash: false}); }); $('#btn-co-nocomplete').live('click', function() { jobCompleted = false; $.mobile.changePage('#page-clockout-deficiency', {changeHash: false}); }); $('#btn-co-nodef').live('click', function() { clockOut(activeJob, { completed:jobCompleted }, DW_JOB_COMPLETED); }); $('#btn-co-otherdef').live('click', function() { $.mobile.changePage('#page-clockout-redtag', {changeHash: false}); }); $('#btn-co-redtag').live('click', function() { clockOut(activeJob, { completed:jobCompleted, redTag:true }, DW_JOB_FOLLOWUP) }); $('#btn-co-noredtag').live('click', function() { $('#page-clockout-resolve').page(); $.mobile.changePage('#page-clockout-resolve', {changeHash: false}); }); $('#btn-clockout-resolve').live('click', function() { switch ($('#page-clockout-resolve :checked').val()) { case 'return': Wednesday, March 7, 12 clockOut(activeJob, { completed:false }, DW_JOB_RETURN);
  • 11.
    search input $('#searchInput form').submit(function(e){ e.preventDefault(); var term = $('#searchInput input').val(), req = $.getJSON('http://search.twitter.com/searc escape(term)); req.then(function(resp) { search data var resultsHtml = $.map(resp.results, function(r) return '<li>' + '<p class="tweet">' + r.text + '</p>' + '<p class="username">' + r.from_user + '</p>' '</li>'; }).join(''); $('#searchResults').html(resultsHtml); search results }); }); Wednesday, March 7, 12
  • 12.
  • 13.
  • 14.
  • 15.
    <script type="text/javascript" src="http://static.a2cdn.net/misc/jquery.js?9"></script> <script type="text/javascript" src="http://static.a2cdn.net/misc/drupal.js?9"></script> <script type="text/javascript" src="/google_analytics/googleanalytics.js?9"></script> <script type="text/javascript" src="/mollom/mollom.js?9"></script> <script type="text/javascript" src="/js/jquery-1.4.2.min.js?9"></script> <script type="text/javascript" src="/js/jquery-ui-1.8.4.custom.min.js?9"></script> <script type="text/javascript" src="/js/jquery.infieldlabel.min.js?9"></script> <script type="text/javascript" src="/js/jquery.nivo.slider.js?9"></script> <script type="text/javascript" src="/js/validations.js?9"></script> <script type="text/javascript" src="/js/site.js?9"></script> <script type="text/javascript" src="/js/noconflict.js?9"></script> Wednesday, March 7, 12
  • 16.
  • 17.
    require.config({ deps: ["main"], paths: { // JavaScript folders libs: "../assets/js/libs", plugins: "../assets/js/plugins", // Libraries jquery: "../assets/js/libs/jquery", underscore: "../assets/js/libs/underscore", backbone: "../assets/js/libs/backbone" } }); Wednesday, March 7, 12
  • 18.
    define([ "components/page", "components/search/input", "components/search/results", ], function(Page, SearchInput, SearchResults) { return { // ... }; }); Wednesday, March 7, 12
  • 19.
    // NAVIGATION function togglePage(section) { // if the clicked section is already the current section AND we're in full page mode // minimize the current tab if (jQuery('#md_tab_'+ section).hasClass('current') && jQuery('#md_tab_'+ section + ' a').hasClass('md_fullpage')) { // alert('clicked section is current section AND fullpage mode is active; teaser should load'); // Minimize jQuery('#md_tabs_navigation a').removeClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideDown('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ jQuery('#md_tabs_navigation a').each(function(){ var thisSection = jQuery(this).html().replace('<span<','').replace('</span<',''); var thisSection_comp = thisSection.toLowerCase().replace(' ','_'); jQuery('#md_body_'+ thisSection_comp).load( '/app/modules/info/loadTeaser.php?sect='+ thisSection_comp, function(){ tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); bodyContent.animate({ height: 'toggle', opacity: 'toggle' },"slow"); } ); }); }); }); jQuery('#expandtabs span').empty().append('Expand Tabs'); } else { // if the clicked section is NOT the current section OR we're NOT in full page mode // then let's go to full page mode and show the whole tab // Maximize // alert('clicked section is not the current section OR full page mode is not active; full section should load'); jQuery('#md_tabs_navigation li').removeClass('current'); jQuery('#md_tab_'+ section).addClass('current'); jQuery('#md_tabs_navigation a').addClass('md_fullpage'); jQuery('.md_body').hide(); jQuery('#md_feature').slideUp('normal',function(){ var bodyContent = jQuery('#md_body_'+ section); bodyContent.fadeOut('normal',function(){ bodyContent.empty(); var pageLoader = 'info/loadSection.php?sect='+ section; if (section == 'contact_us') { pageLoader = 'contact/loadContactForm.php?form_id=1'; } bodyContent.load('/app/modules/'+ pageLoader,function(){ // ADD THICKBOXES tb_init('.md_body a.thickbox, .md_body area.thickbox, .md_body input.thickbox'); $recent_news_links = jQuery('ul.md_news li a.recent_news_link'); $recent_news_links .unbind('click') .each(function(){ var hrefMod = this.href; hrefMod = hrefMod.replace(/article/,'loadNews').replace(/storyid/,'id'); this.href = hrefMod; }) .click(function(){ var t = this.title || this.name || null; var a = this.href || this.alt; var g = this.rel || false; tb_show(t,a,g); this.blur(); return false; }); // ACCORDION jQuery('div.applemenu div.submenu').hide(); jQuery('div.applemenu div.silverheader < a').click( Wednesday, March 7, 12 function(){
  • 20.
    component controller component service components display data, observe input, and broadcast messages that controllers can react to; may also provide an API for updating Wednesday, March 7, 12
  • 21.
    component controller component service controllers set up components and broker communications between them, eliminating the need for direct communication Wednesday, March 7, 12
  • 22.
    component controller component service services expose an API for controllers to retrieve and persist data Wednesday, March 7, 12
  • 23.
  • 24.
    controller twitter search Wednesday, March 7, 12
  • 25.
    components display data,observe input, and broadcast messages that controllers can react to; may also provide an API for updating Wednesday, March 7, 12
  • 26.
    define([ "components/base" ], function(Component) { return Component({ template : "app/templates/search/input.html", className : 'search-input', events : { "submit form" : "_onSubmit" }, _onSubmit : function(e) { e.preventDefault(); var term = $.trim(this.$el.find('input')[0].value); if (!term) { return; } this.trigger('search', term); } }); }); Wednesday, March 7, 12
  • 27.
    <form> <input placeholder="enter your search term"> <button>submit</button> </form> Wednesday, March 7, 12
  • 28.
    controllers set upcomponents and broker communications between them, eliminating the need for direct communication Wednesday, March 7, 12
  • 29.
    <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Searchr</title> <!-- Application styles --> <link rel="stylesheet" href="/assets/css/index.css"> </head> <body> <!-- Main container --> <div role="main" id="main"></div> <!-- Application source --> <script data-main="app/config" src="/assets/js/libs/require.js"></ script> </body> </html> Wednesday, March 7, 12
  • 30.
    var Router =Backbone.Router.extend({ routes: { "": "twitter", ":hash": "twitter" }, twitter : function(hash) { SearchController.init(); } }); Wednesday, March 7, 12
  • 31.
    define([ "components/page", "components/search/input", "components/search/results", "services/twitter" ], function(Page, SearchInput, SearchResults, twitterService) { return { init : function() { this.page = new Page({ template : 'app/templates/pages/search.html' }); this.page.render().then(_.bind(this.setupPage, this)); }, setupPage : function() { var p = this.page; this.searchInput = p.place(new SearchInput(), 'searchInput'); this.searchResults = p.place(new SearchResults(), 'searchResults'); this.searchInput.on('search', _.bind(this.handleSearch, this)); }, handleSearch : function(term) { twitterService.query(term).then(_.bind(this.showResults, this)); }, showResults : function(results) { this.searchResults.show(results); } }; }); Wednesday, March 7, 12
  • 32.
    services expose anAPI for controllers to interact with data Wednesday, March 7, 12
  • 33.
    define([ "use!backbone" ], function(Backbone) { var TwitterResult = Backbone.Model.extend({ // ... }); var TwitterResults = Backbone.Collection.extend({ type : 'twitter', model : TwitterResult }); return { query : function(term) { var req = $.getJSON('http://search.twitter.com/search.json?callback=?&q=' + escape(term)), dfd = $.Deferred(); req.then(function(resp) { dfd.resolve(new TwitterResults(resp.results)); }); return dfd.promise(); } }; }); Wednesday, March 7, 12
  • 34.
    “Writing to beread means writing code ... with the idea that someone else will read it. is fact alone will make you edit and think of better ways to solve the problem you have at hand.” Stoyan Stefanov, “JavaScript Patterns” Wednesday, March 7, 12
  • 35.
  • 36.